精华内容
下载资源
问答
  • FileEntropy:按类型组织文件,删除重复项,并减少索引内容的熵
  • InnoDB索引

    万次阅读 2019-08-05 16:30:09
    1.概述 InnoDB存储引擎支持一下几种索引 B+ 树索引 全文索引 哈希索引 2.B+ 树索引 ...B+树索引可以分为聚集索引(clustered inex)和辅助索引(secondary),其内部全是B+树结构,高度平衡。...

    1.概述

    InnoDB存储引擎支持一下几种索引

    • B+ 树索引
    • 全文索引
    • 哈希索引

    本文参考了姜承尧先生的《MySQL技术内幕InnoDB存储引擎》一书

    2.B+ 树索引

    B+ 树索引并不能找到给定字符的具体位置,而是将字符所在的页读取到内存中,然后再内存中查找数据。B+树中的B不是代表二叉(binary), 而是代表平衡(balance)

    B+树索引可以分为聚集索引(clustered inex)和辅助索引(secondary),其内部全是B+树结构,高度平衡。聚集索引与辅助索引的区别是 : 叶子节点存放的是否是一整行的信息

    • 2.1 聚集索引

    聚集索引就是按照每张表的主键构建一颗B+树,叶子节点中存放的即为整张表的行记录数据,也将聚集索引的叶子节点称为数据页。这个特性决定了索引组织表中数据也是索引的一部分,每个数据页都通过一个双向链表进行连接

    由于每个数据页只能按一颗B+树进行排序,因此每张表只能有一个聚集索引,查询优化器倾向于采用聚集索引,因为聚集索引能在B+树索引的叶子节点上直接找到数据。

    聚集索引另一个好处是:他对于主键的排序查找和范围查找速度非常快,叶子节点就是用户查找的数据

    • 2.2 辅助索引

    辅助索引(也称非聚集索引)的叶子节点并不包含行记录的全部数据,叶子节点除了包含键值以外每个叶子节点中的索引行还包含了一个书签(bookmark),书签用来告诉InnoDB存储引擎哪里可以找到与索引相对应的行数据,因此书签就是相应行数据的聚集索引键。

    辅助索引并不会影响聚集索引,所以每张表可以有多个辅助索引。
    当通过辅助索引查找数据时,InnoDB存储引擎会遍历辅助索引并通过叶级别的指针获取指向主键索引的主键,然后通过主键索引来找到一个完整的行记录。

    3.Cardinality 值

    • Cardinality 值的作用

    对于查询条件中出现的列不一定都需要添加索引,对于反复出现重复字段的,例如性别,类型、地区等重复性高的列,他们取值范围很小,属于低选择性,这时添加索引是没有必要的,相反,对于数据几乎没有重复的字段,属于高选择性的添加B+树索引最合适。

    查看索引是否属于高选择性,可以通过SHOW INDEX 中的Cardinality 值,它表示索引中不重复记录数量的预估值。他并不是一个准确值

    4.B+ 树索引的使用

    • 4.1 联合索引

    创建方法和单个索引创建方法一样,不同之处是有多个索引列。

    CREATE TABLE t{
    	a INT,
    	b INT,
    	PRIMARY KEY (a),
    	KEY idx_a_b (a,b)
    } ENGINE = INNODB
    

    索引 idx_a_b 是联合索引,联合的列为(a,b)。

    何时需要使用联合索引呢?
    先看联合索引底层的实现, 多个键值对的B+树和单个键的B+树没有什么不同,都是按顺序存放的。
    在这里插入图片描述
    这样就限制了查找方式,如果我们这样查SELECT * FROM t WHERE a = xxx and b = xxx显然这样能使用这个联合索引找到(a,b)
    但是我们使用SELECT * FROM t WHERE b = xxx查找b,就不会通过索引来查找,因为构建的B+是先根据a的大小进行排序的,索引通过b找不到位置。

    • 4.2 覆盖索引

    覆盖索引是从辅助索引中就可以得到查询记录,不需要查询聚集索引,因为辅助索引是不包含整行信息的,所以其大小远小于聚集索引。

    • 4.3 优化器选择不使用索引的情况

    这种情况多发生于有固定查询范围并且需要对数据整行数据进行查询,例如

    SELECT * FROM TABLES WHERE ID>10000 AND ID<102000
    
    • 4.4 Multi-Range Read(MRR) 优化

    优化的目的是减少磁盘的随机访问,并将随机访问转化为较为有序的数据访问,这对于IO-Bound类型的SQL查询语句带来性能的极大提升。MRR优化可用于range、ref、eq_ref类型的查询。

    • MRR使数据访问变得较为顺序. 在查询辅助索引时, 首先根据得到的查询结果, 按照主键顺序进行排序, 并按照主键排序的顺序进行书签查找
    • 减少缓冲池中页被替换的次数
    • 批量处理对键值的查询操作

    对于InnoDB 和 MyISAM 存储引擎的范围查询和JOIN 查询操作,MRR的工作方式如下:

    • 将查询得到的辅助索引键存放于一个缓存中,这时缓存中的数据时根据辅助索引键值排序的
    • 将缓存中的键值根据RowID进行排序
    • 根据RowID的排序顺序来访问实际的数据文件
    —————————————————————————————————————————————————————————————————————————————————
    
    • 4.5 Index Condition Pushdown (ICP)优化

    之前版本进行索引查询的时候,首先根据索引来查找记录,然后在根据WHERE条件来过滤记录,支持Index Condition Pushdown 后MySQL数据库会在取出索引的同时,判断是否可以进行WHERE条件的过滤,也就是将WHERE的部分过滤操作放在了存储引擎层。

    5 全文检索

    B+树索引的特点是支持前缀进行查找,例如SELECT * FROM blog WHERE content like 'xxx%'。对于SELECT * FROM blog WHERE content like '%xxx',并不支持,这种模式会导致InnoDB扫描整个表,速度会非常慢。那么现在就需要一种方案解决这个问题。那就是全文索引。InnoDB 1.2版本开始,已经全面支持了全文索引(Full-Text Search )。全文检索是将存储于数据库中的整本书或整篇文章中的任意内容单词查找出来的技术。它可以根据需要获得全文中有关章、节、段、句、词等信息,也可以进行各种统计和分析。全文索引使用倒排索引实现辅助表中存储了单词与单词自身在一个或多个文档中的映射。将整个表中单词所在的行及其位置全部索引出来。这样在需要从中检索出行中释放含有某个单词就很快了。

    • 5.1 倒排索引

    在这里插入图片描述
    InnoDB存储引擎采用 full inverted index 的方式实现全文检索。

    展开全文
  • 索引

    千次阅读 2013-02-26 20:36:02
     常见的索引有B-TREE索引、位图索引、全文索引,位图索引一般用于数据仓库应用,全文索引由于使用较少,这里不深入介绍。B-TREE索引包括很多扩展类型,如组合索引、反向索引、函数索引等等,以下是B-TREE索引的简单...
    一、什么是索引
    
     常见的索引有B-TREE索引、位图索引、全文索引,位图索引一般用于数据仓库应用,全文索引由于使用较少,这里不深入介绍。B-TREE索引包括很多扩展类型,如组合索引、反向索引、函数索引等等,以下是B-TREE索引的简单介绍:

     B-TREE索引也称为平衡树索引(Balance Tree),它是一种按字段排好序的树形目录结构,主要用于提升查询性能和唯一约束支持B-TREE索引的内容包括根节点、分支节点、叶子节点。B-TREE索引如下图所示:


                                                           

    如果我们把一个表的内容认为是一本字典,那索引就相当于字典的目录,如下图所示:


                                                        

     图中是一个字典按部首+笔划数的目录,相当于给字典建了一个按部首+笔划的组合索引。
    一个表中可以建多个索引,就如一本字典可以建多个目录一样(按拼音、笔划、部首等等)。
    一个索引也可以由多个字段组成,称为组合索引,如上图就是一个按部首+笔划的组合目录。


    二、索引的作用:
    索引是什么我们先说到这里,以后我们会进一步的讲述,索引不单是对列排序那么简单,在排序的基础上,它还按数据结构中B树的方式存储,这个就是后话了。下面我们说一下,索引有什么用。索引的作用,只有两个字,就是“查找”。它是为了加快查找速度。对于无序的一组数据,想在其中查找某一个数,只能逐个进行对比。而在有序的数列中查找某一个数,则有许多种方法,比如折半查找等等,ORACLE采用的是B树法。通常在表中某一列搜索数据时,如果此列上建立的有索引,ORACLE会自动的在索引中利用搜索算法,快速的找到要查找的值,再取出对应行的行地址,根据此行地址一表中取出行数据。这个速度比没有索引时要快很多倍。
    下面,我们先建立一个索引,仍后试一下使用索引查找数据,比不用索引时速度到底有什么不同。


    三、索引的创建
    索引的创建语法:CREATE INDEX 索引名 ON 表名(列1,列2,……)
    索引可以按照一个表的多个列创建,这样的索引称为复合索引。对于复合索引,ORACLE先按列1排序,如果列1的值有重复的,再按列2排序,等等。
    下面我先创建一个比较大一点的表,行数多一点,这样我们对比通过索引查找和无索引时的查找,它们间的差别更明显。


    gyj@OCM> drop table t5;


    Table dropped.


    gyj@OCM> create table t5 as select * from dba_objects;


    Table created.


    我通过DBA_OJECTS表,创建T5表。DBA_OBJECTS表存储数据库中所有对象的相关信息,行比较多。但是这样行还是不够多


    gyj@OCM> insert into t5 select * from t5;


    72804 rows created.


    gyj@OCM> /


    145608 rows created.


    gyj@OCM> /


    291216 rows created.


    gyj@OCM> /


    582432 rows created.


    gyj@OCM> commit;


    Commit complete.


     我再如上将T5中的行读出来,插入到T5中,反复几次,让T5表的行数至少有一百万行左右。
    gyj@OCM>  select count(*) from t5;


      COUNT(*)
    ----------
       1164864


    最终,我的T5表中的100多万行。
    然后,我们打开ORACLE的计数器:


    gyj@OCM> set timing on   
    gyj@OCM> select * from t5 where object_name='T5';
    (结果略)
    Elapsed: 00:00:00.25


    用时0.25秒。因为ORACLE会在内存中缓存一些信息,因此,上面的命令,应该多执行几次,等待每次执行时间没有太大的变化,取这个执行时间稳定下来的结果,这样比较准确。我这里,没有建索引时,用时0.15秒。
    因为是在OBJECT_NAME列查找,我就在OBJECT_NAME列上,建立一个索引:


    gyj@OCM> create index idx_t5_object_name on t5(object_name);


    Index created.


    创建索引的命令,非常简单,我就不再说了。下面我已经以T5表的OBJECT_NANE列为准,创建了索引,通常说我为T5表的OBJECT_NAME列创建了索引,下面再次在这个列中进行查询,


    gyj@OCM> select * from t5 where object_name='T5';
    (结果略)
    Elapsed: 00:00:00.01


    当每次的执行时间稳定下来后,当有索引后,执行时间是0.01秒。
    执行时间有非常明显的下降,而且,随着表中行数的进一步增加,按索引搜索和直接在表中搜索所耗时间,差距会更大。使用索引进行查找的优势,会更明显。
    索引的使用是透明的,在这条“select * from t5 where object_name='T5'”语句中,我们完全看不到有索引的影子,只要“WHERE 条件”所涉及的列,创建有对应的索引,ORACLE会自动的选择使用索引。
    由于索引对于加快查找速度有显著效果,我们可以在经常需要查找的列上,都创建索引。但是索引的数量也不适合太多。因为每一个索引都会有相应的索引维护操作。你每在表中插入一行,ORACLE会将相应列的值,插入进索引。每删除一行,或修改索引对应列的值,ORACLE也都要对应的修改索引。在对表进行DML时,所引起的对索引的DML操作,就是索引维护。它加重了DML操作时的负担,因此,索引是不适合建太多的,否则,DML操作时的速度将更慢。


    四、基于函数的索引
    我们仍以刚才的例子为准,在OBJECT_NAME列上有索引。下面,我们试一下如下命令:
    gyj@OCM> select * from t5 where lower(object_name)='t5';
    (结果省略)
    Elapsed: 00:00:00.64


    它的稳定时间,是0.64秒左右。虽然我们的条件仍然涉及到了OBJECT_NAME列,但上面的命令显然没有使用索引。因为它的执行时间很慢。在使用索引查找时,速度只有0.01秒左右。现在是0.64秒。没有使用索引的原因是这语句的条件:lower(object_name)='t5' ,先把OBEJCT_NAME转为了小写,再和小写的t5比较。一旦像这样对列名进行了处理,索引将不会被使用。如果经常要对此列转换为小写再比较,为了使用索引,你可以专门创建“基于函数的索引”,命令如下:


    gyj@OCM> create index idx_f_t5_obname on t5(lower(object_name));


    Index created.


    和上面创建普通索引的命令基本相同,只是将列名换为了LOWER(列) ,好,下面再试查询的速度有没有提高:


    gyj@OCM> select * from t5 where lower(object_name)='t5';
    (结果省略)
    Elapsed: 00:00:00.01


    稳定耗时是00: 00: 00.01 。0.01秒,比没有基于函数索引前的0.64秒快了很多倍。
    这就是基于函数的索引。


    五、索引创建指南

    这是一个非常复杂的话题,需要对业务及数据充分分析后再能得出结果。主键及外键通常都要有索引,其它需要建索引的字段应满足以下条件,如下图所示:

                                                               

     1、字段的选择性很好(主键最佳)。
     2、字段出现在查询条件中,并且查询条件可以使用索引。
     3、语句执行频率高,一天会有几千次以上。
     4、通过字段条件可筛选的记录集很小。


    六、删除索引
    索引会带来额外的索引维护操作,因此,不经常使用的索引,我们应该及时的删除它,以减少DML操作时的负担。删除索引命令如下,把上面刚建的基于函数的索引删掉:


    gyj@OCM> drop index idx_f_t5_obname;                    


    Index dropped.





    **********本博客所有内容均为原创,如有转载请注明作者和出处!!!**********
    Name:    guoyJoe

    QQ:        252803295

    Email:    oracledba_cn@hotmail.com

    Blog:      http://blog.csdn.net/guoyJoe

    ITPUB:   http://www.itpub.net/space-uid-28460966.html

    OCM:     http://education.oracle.com/education/otn/YGuo.HTM
     _____________________________________________________________
    加群验证问题:哪些SGA结构是必需的,哪些是可选的?否则拒绝申请!!!

    答案在:http://blog.csdn.net/guoyjoe/article/details/8624392

    Oracle@Paradise  总群:127149411

    Oracle@Paradise No.1群:177089463(已满)

    Oracle@Paradise No.2群:121341761

    Oracle@Paradise No.3群:140856036


    展开全文
  • mysql索引

    千次阅读 2019-08-18 10:33:10
    一句话简单来说,索引的出现其实就是为了提高数据查询的效率,就像书的目录一样。一本 500 页的书,如果想快速找到其中的某一个知识点,在不借助目录的情况下,那估计得找一会儿。同样,对于数据库的表而言,索引...

    一句话简单来说,索引的出现其实就是为了提高数据查询的效率,就像书的目录一样。一本 500 页的书,如果想快速找到其中的某一个知识点,在不借助目录的情况下,那估计得找一会儿。同样,对于数据库的表而言,索引其实就是它的“目录”。

    1、索引的常见模型

    索引的出现是为了提高查询效率,但是实现索引的方式却有很多种,所以这里也就引入了索引模型的概念。可以用于提高读写效率的数据结构很多,这里我先给你介绍三种常见、也比较简单的数据结构,它们分别是哈希表、有序数组和搜索树。

    下面主要从使用的角度,简单分析一下这三种模型的区别:

    1.1 哈希表

    哈希表是一种以键-值(key-value)存储数据的结构,我们只要输入待查找的值即 key,就可 以找到其对应的值即 Value。哈希的思路很简单,把值放在数组里,用一个哈希函数把 key 换 算成一个确定的位置,然后把 value 放在数组的这个位置。

    不可避免地,多个 key 值经过哈希函数的换算,会出现同一个值的情况。处理这种情况的一种方法是,拉出一个链表。

    假设,你现在维护着一个身份证信息和姓名的表,需要根据身份证号查找对应的名字,这时对应的哈希索引的示意图如下所示:
    在这里插入图片描述
    图中,User2 和 User4 根据身份证号算出来的值都是 N,但没关系,后面还跟了一个链表。假 设,这时候你要查 ID_card_n2 对应的名字是什么,处理步骤就是:首先,将 ID_card_n2 通过 哈希函数算出 N;然后,按顺序遍历,找到 User2。

    需要注意的是,图中四个 ID_card_n 的值并不是递增的,这样做的好处是增加新的 User 时速度 会很快,只需要往后追加。但缺点是,因为不是有序的,所以哈希索引做区间查询的速度是很慢的。

    你可以设想下,如果你现在要找身份证号在 [ID_card_X, ID_card_Y] 这个区间的所有用户,就必须全部扫描一遍了。

    所以,哈希表这种结构适用于只有等值查询的场景,比如 Memcached 及其他一些 NoSQL 引擎。

    1.2 有序数组

    有序数组在等值查询和范围查询场景中的性能就都非常优秀。还是上面这个根据身份证号查名字的例子,如果我们使用有序数组来实现的话,示意图如下所示:
    在这里插入图片描述

    这里我们假设身份证号没有重复,这个数组就是按照身份证号递增的顺序保存的。这时候如果你 要查 ID_card_n2 对应的名字,用二分法就可以快速得到,这个时间复杂度是 O(log(N))。

    同时很显然,这个索引结构支持范围查询。你要查身份证号在 [ID_card_X, ID_card_Y] 区间的 User,可以先用二分法找到 ID_card_X(如果不存在 ID_card_X,就找到大于 ID_card_X 的第 一个 User),然后向右遍历,直到查到第一个大于 ID_card_Y 的身份证号,退出循环。

    如果仅仅看查询效率,有序数组就是最好的数据结构了。但是,在需要更新数据的时候就麻烦了,你往中间插入一个记录就必须得挪动后面所有的记录,成本太高。

    所以,有序数组索引只适用于静态存储引擎,比如你要保存的是 2017 年某个城市的所有人口信息,这类不会再修改的数据。

    1.3 二叉搜索树

    二叉搜索树也是课本里的经典数据结构了。还是上面根据身份证号查名字的例子,如果我们用二叉搜索树来实现的话,示意图如下所示:
    在这里插入图片描述

    二叉搜索树的特点是:每个节点的左儿子小于父节点,父节点又小于右儿子。这样如果你要查 ID_card_n2 的话,按照图中的搜索顺序就是按照 UserA -> UserC -> UserF -> User2 这个路径得到。这个时间复杂度是 O(log(N))。

    当然为了维持 O(log(N)) 的查询复杂度,你就需要保持这棵树是平衡二叉树。为了做这个保证,更新的时间复杂度也是 O(log(N))。

    树可以有二叉,也可以有多叉。多叉树就是每个节点有多个儿子,儿子之间的大小保证从左到右递增。二叉树是搜索效率最高的,但是实际上大多数的数据库存储却并不使用二叉树。其原因是,索引不止存在内存中,还要写到磁盘上。

    你可以想象一下一棵 100 万节点的平衡二叉树,树高 20。一次查询可能需要访问 20 个数据块。在机械硬盘时代,从磁盘随机读一个数据块需要 10 ms 左右的寻址时间。也就是说,对于 一个 100 万行的表,如果使用二叉树来存储,单独访问一个行可能需要 20 个 10 ms 的时间, 这个查询可真够慢的。

    为了让一个查询尽量少地读磁盘,就必须让查询过程访问尽量少的数据块。那么,我们就不应该 使用二叉树,而是要使用“N 叉”树。这里,“N 叉”树中的“N”取决于数据块的大小。

    以 InnoDB 的一个整数字段索引为例,这个 N 差不多是 1200。这棵树高是 4 的时候,就可以 存 1200 的 3 次方个值,这已经 17 亿了。考虑到树根的数据块总是在内存中的,一个 10 亿行的表上一个整数字段的索引,查找一个值最多只需要访问 3 次磁盘。其实,树的第二层也有很大概率在内存中,那么访问磁盘的平均次数就更少了。

    N 叉树由于在读写上的性能优点,以及适配磁盘的访问模式,已经被广泛应用在数据库引擎中了。

    不管是哈希还是有序数组,或者 N 叉树,它们都是不断迭代、不断优化的产物或者解决方案。 数据库技术发展到今天,跳表、LSM 树等数据结构也被用于引擎设计中。

    在学习数据库的时候心里要有个概念,数据库底层存储的核心就是基于这些数据模型的。每碰到一个新数据库,我们需要先关注它的数据模型,这样才能从理论上分析出这个数据库的适用场景。

    2、InnoDB 的索引模型

    在 MySQL 中,索引是在存储引擎层实现的,所以并没有统一的索引标准,即不同存储引擎的索引的工作方式并不一样。而即使多个存储引擎支持同一种类型的索引,其底层的实现也可能不同。由于 InnoDB 存储引擎在 MySQL 数据库中使用最为广泛,所以下面就以 InnoDB 为例,分析一下其中的索引模型。

    在 InnoDB 中,表都是根据主键顺序以索引的形式存放的,这种存储方式的表称为索引组织表。又因为前面我们提到的,InnoDB 使用了 B+ 树索引模型,所以数据都是存储在 B+ 树中的。

    每一个索引在 InnoDB 里面对应一棵 B+ 树。

    假设,我们有一个主键列为 ID 的表,表中有字段 k,并且在 k 上有索引。这个表的建表语句是:

    mysql> create table T(
    id int primary key,k int not null,name varchar(16),
    index (k))engine=InnoDB;
    

    表中 R1~R5 的 (ID,k) 值分别为 (100,1)、(200,2)、(300,3)、(500,5) 和 (600,6),两棵树的示例示意图如下。
    在这里插入图片描述
    从图中不难看出,根据叶子节点的内容,索引类型分为主键索引和非主键索引。
    主键索引的叶子节点存的是整行数据。在 InnoDB 里,主键索引也被称为聚簇索引(clustered index)。
    非主键索引的叶子节点内容是主键的值。在 InnoDB 里,非主键索引也被称为二级索引 (secondary index)。

    根据上面的索引结构说明,我们来讨论一个问题:基于主键索引和普通索引的查询有什么区别?

    • 如果语句是 select * from T where ID=500,即主键查询方式,则只需要搜索 ID 这棵 B+ 树;
    • 如果语句是 select * from T where k=5,即普通索引查询方式,则需要先搜索 k 索引树, 得到 ID 的值为 500,再到 ID 索引树搜索一次。这个过程称为回表。

    也就是说,基于非主键索引的查询需要多扫描一棵索引树。因此,我们在应用中应该尽量使用主键查询。

    3、索引维护

    B+ 树为了维护索引有序性,在插入新值的时候需要做必要的维护。以上面这个图为例,如果插 入新的行 ID 值为 700,则只需要在 R5 的记录后面插入一个新记录。如果新插入的 ID 值为 400,就相对麻烦了,需要逻辑上挪动后面的数据,空出位置。

    而更糟的情况是,如果 R5 所在的数据页已经满了,根据 B+ 树的算法,这时候需要申请一个新 的数据页,然后挪动部分数据过去。这个过程称为页分裂。在这种情况下,性能自然会受影响。

    除了性能外,页分裂操作还影响数据页的利用率。原本放在一个页的数据,现在分到两个页中, 整体空间利用率降低大约 50%。

    当然有分裂就有合并。当相邻两个页由于删除了数据,利用率很低之后,会将数据页做合并。合
    并的过程,可以认为是分裂过程的逆过程。

    基于上面的索引维护过程说明,我们来讨论一个案例:

    你可能在一些建表规范里面见到过类似的描述,要求建表语句里一定要有自增主键。当然事无绝对,我们来分析一下哪些场景下应该使用自增主键,而哪些场景下不应该。

    自增主键是指自增列上定义的主键,在建表语句中一般是这么定义的: NOT NULL PRIMARY KEY AUTO_INCREMENT。

    插入新记录的时候可以不指定 ID 的值,系统会获取当前 ID 最大值加 1 作为下一条记录的 ID值。

    也就是说,自增主键的插入数据模式,正符合了我们前面提到的递增插入的场景。每次插入一条
    新记录,都是追加操作,都不涉及到挪动其他记录,也不会触发叶子节点的分裂。

    而有业务逻辑的字段做主键,则往往不容易保证有序插入,这样写数据成本相对较高。

    除了考虑性能外,我们还可以从存储空间的角度来看。假设你的表中确实有一个唯一字段,比如
    字符串类型的身份证号,那应该用身份证号做主键,还是用自增字段做主键呢?

    由于每个非主键索引的叶子节点上都是主键的值。如果用身份证号做主键,那么每个二级索引的 叶子节点占用约 20 个字节,而如果用整型做主键,则只要 4 个字节,如果是长整型(bigint) 则是 8 个字节。

    显然,主键长度越小,普通索引的叶子节点就越小,普通索引占用的空间也就越小。

    所以,从性能和存储空间方面考量,自增主键往往是更合理的选择。

    有没有什么场景适合用业务字段直接做主键的呢?还是有的。比如,有些业务的场景需求是这样
    的:

    1. 只有一个索引;
    2. 该索引必须是唯一索引。
      这就是典型的 KV 场景。由于没有其他索引,所以也就不用考虑其他索引的叶子节点大小的问题。

    这时候我们就要优先考虑上一段提到的“尽量使用主键查询”原则,直接将这个索引设置为主
    键,可以避免每次查询需要搜索两棵树。

    4、索引优化

    在下面这个表 T 中,如果我执行 select * from T where k between 3 and 5,需要执行几次树的搜索操作,会扫描多少行?

    create table T (
    ID int primary key,
    k int NOT NULL DEFAULT 0,
    s varchar(16) NOT NULL DEFAULT '',
    index k(k)) engine=InnoDB;
    
    insert into T values(100,1, 'aa'),(200,2,'bb'),(300,3,'cc'),(500,5,'ee'),(600,6,'ff'),(700,7,'gg')
    

    在这里插入图片描述

    现在,我们一起来看看这条 SQL 查询语句的执行流程:

    1. 在 k 索引树上找到 k=3 的记录,取得 ID = 300;
    2. 再到 ID 索引树查到 ID=300 对应的 R3;
    3. 在 k 索引树取下一个值 k=5,取得 ID=500;
    4. 再回到 ID 索引树查到 ID=500 对应的 R4;
    5. 在 k 索引树取下一个值 k=6,不满足条件,循环结束。

    在这个过程中,回到主键索引树搜索的过程,我们称为回表。可以看到,这个查询过程读了 k 索引树的 3 条记录(步骤 1、3 和 5),回表了两次(步骤 2 和 4)。

    在这个例子中,由于查询结果所需要的数据只在主键索引上有,所以不得不回表。那么,有没有可能经过索引优化,避免回表过程呢?

    在这个例子中,由于查询结果所需要的数据只在主键索引上有,所以不得不回表。那么,有没有可能经过索引优化,避免回表过程呢?

    4.1 覆盖索引

    如果执行的语句是 select ID from T where k between 3 and 5,这时只需要查 ID 的值,而 ID 的值已经在 k 索引树上了,因此可以直接提供查询结果,不需要回表。也就是说,在这个查询里面,索引 k 已经“覆盖了”我们的查询需求,我们称为覆盖索引。

    由于覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。

    需要注意的是,在引擎内部使用覆盖索引在索引 k 上其实读了三个记录,R3~R5(对应的索引 k 上的记录项),但是对于 MySQL 的 Server 层来说,它就是找引擎拿到了两条记录,因此 MySQL 认为扫描行数是 2。

    基于上面覆盖索引的说明,我们来讨论一个问题:在一个市民信息表上,是否有必要将身份证号和名字建立联合索引?

    CREATE TABLE `tuser` (
    `id` int(11) NOT NULL,
    `id_card` varchar(32) DEFAULT NULL,
    `name` varchar(32) DEFAULT NULL,
    `age` int(11) DEFAULT NULL,
    `ismale` tinyint(1) DEFAULT NULL,
    PRIMARY KEY (`id`),
    KEY `id_card` (`id_card`),
    KEY `name_age` (`name`,`age`)
    ENGINE=InnoDB
    

    我们知道,身份证号是市民的唯一标识。也就是说,如果有根据身份证号查询市民信息的需求,我们只要在身份证号字段上建立索引就够了。而再建立一个(身份证号、姓名)的联合索引,是不是浪费空间?

    我们知道,身份证号是市民的唯一标识。也就是说,如果有根据身份证号查询市民信息的需求,我们只要在身份证号字段上建立索引就够了。而再建立一个(身份证号、姓名)的联合索引,是不是浪费空间?

    如果现在有一个高频请求,要根据市民的身份证号查询他的姓名和年龄,这个联合索引就有意义了。它可以在这个高频请求上用到覆盖索引,不再需要回表查整行记录,减少语句的执行时间。

    当然,索引字段的维护总是有代价的。因此,在建立冗余索引来支持覆盖索引时就需要权衡考虑了。这正是业务 DBA,或者称为业务数据架构师的工作。

    4.2 最左前缀原则

    看到这里你一定有一个疑问,如果为每一种查询都设计一个索引,索引是不是太多了。如果我现在要按照市民的身份证号去查他的家庭地址呢?虽然这个查询需求在业务中出现的概率不高,但总不能让它走全表扫描吧?反过来说,单独为一个不频繁的请求创建一个(身份证号,地址)的索引又感觉有点浪费。应该这么做呢?

    B+ 树这种索引结构,可以利用索引的“最左前缀”,来定位记录。

    为了直观地说明这个概念,我们用(name,age)这个联合索引来分析。

    在这里插入图片描述
    可以看到,索引项是按照索引定义里面出现的字段顺序排序的。

    当你的逻辑需求是查到所有名字是“张三”的人时,可以快速定位到 ID4,然后向后遍历得到所有需要的结果。

    如果你要查的是所有名字第一个字是“张”的人,你的 SQL 语句的条件是"where name like ‘张 %’"。这时,你也能够用上这个索引,查找到第一个符合条件的记录是 ID3,然后向后遍历,直到不满足条件为止。

    可以看到,不只是索引的全部定义,只要满足最左前缀,就可以利用索引来加速检索。这个最左前缀可以是联合索引的最左 N 个字段,也可以是字符串索引的最左 M 个字符。

    基于上面对最左前缀索引的说明,我们来讨论一个问题:在建立联合索引的时候,如何安排索引 内的字段顺序。

    这里我们的评估标准是,索引的复用能力。因为可以支持最左前缀,所以当已经有了 (a,b) 这个联合索引后,一般就不需要单独在 a 上建立索引了。因此,第一原则是,如果通过调整顺序,可以少维护一个索引,那么这个顺序往往就是需要优先考虑采用的。

    所以现在你知道了,这段开头的问题里,我们要为高频请求创建 (身份证号,姓名)这个联合索引,并用这个索引支持“根据身份证号查询地址”的需求。

    那么,如果既有联合查询,又有基于 a、b 各自的查询呢?查询条件里面只有 b 的语句,是无法使用 (a,b) 这个联合索引的,这时候你不得不维护另外一个索引,也就是说你需要同时维护 (a,b)、(b) 这两个索引。

    这时候,我们要考虑的原则就是空间了。比如上面这个市民表的情况,name 字段是比 age 字段大的 ,那我就建议你创建一个(name,age) 的联合索引和一个 (age) 的单字段索引。

    4.3 索引下推

    上一段我们说到满足最左前缀原则的时候,最左前缀可以用于在索引中定位记录。这时,你可能要问,那些不符合最左前缀的部分,会怎么样呢?

    我们还是以市民表的联合索引(name, age)为例。如果现在有一个需求:检索出表中“名字第 一个字是张,而且年龄是 10 岁的所有男孩”。那么,SQL 语句是这么写的:

    select * from tuser where name like '张 %' and age=10 and ismale=1;
    

    你已经知道了前缀索引规则,所以这个语句在搜索索引树的时候,只能用 “张”,找到第一个满足条件的记录 ID3。当然,这还不错,总比全表扫描要好。

    然后判断其他条件是否满足。

    在 MySQL 5.6 之前,只能从 ID3 开始一个个回表。到主键索引上找出数据行,再对比字段值。而 MySQL 5.6 引入的索引下推优化(index condition pushdown), 可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。

    无下推索引执行过程:
    在这里插入图片描述

    索引下推执行流程:
    在这里插入图片描述

    每一个虚线箭头表示回表一次。

    在无下推索引执行过程,在 (name,age) 索引里面我特意去掉了 age 的值,这个过程 InnoDB 并不会去看 age 的值,只是按顺序把“name 第一个字是’张’”的记录一条条取出来回表。因此,需要回表 4 次。

    在索引下推执行流程,InnoDB 在 (name,age) 索引内部就判断了 age 是否等于 10,对于不等 于 10 的记录,直接判断并跳过。在我们的这个例子中,只需要对 ID4、ID5 这两条记录回表取 数据判断,就只需要回表 2 次。

    5、面试题

    (1)如何避免长事务对业务的影响?
    这个问题,我们可以从应用开发端和数据库端来看。
    首先,从应用开发端来看:

    1. 确认是否使用了 set autocommit=0。这个确认工作可以在测试环境中开展,把 MySQL 的 general_log 开起来,然后随便跑一个业务逻辑,通过 general_log 的日志来确认。一般框架如果会设置这个值,也就会提供参数来控制行为,你的目标就是把它改成 1。
    2. 确认是否有不必要的只读事务。有些框架会习惯不管什么语句先用 begin/commit 框起 来。我见过有些是业务并没有这个需要,但是也把好几个 select 语句放到了事务中。这种 只读事务可以去掉。
    3. 业务连接数据库的时候,根据业务本身的预估,通过 SET MAX_EXECUTION_TIME 命令, 来控制每个语句执行的最长时间,避免单个语句意外执行太长时间。

    其次,从数据库端来看:

    1. 监控 information_schema.Innodb_trx 表,设置长事务阈值,超过就报警 / 或者 kill;
    2. Percona 的 pt-kill 这个工具不错,推荐使用;
    3. 在业务功能测试阶段要求输出所有的 general_log,分析日志行为提前发现问题;
    4. 如果使用的是 MySQL 5.6 或者更新版本,把 innodb_undo_tablespaces 设置成 2(或更大的值,用于设定创建的undo表空间的个数)。如果真的出现大事务导致回滚段过大,这样设置后清理起来更方便。

    (2)实际上主键索引也是可以使用多个字段的。如下:

    CREATE TABLE `geek` (
    `a` int(11) NOT NULL,
    `b` int(11) NOT NULL,
    `c` int(11) NOT NULL,
    `d` int(11) NOT NULL,
    PRIMARY KEY (`a`,`b`),
    KEY `ca` (`c`,`a`),
    KEY `cb` (`c`,`b`)
    )
    ENGINE=InnoDB;
    

    由于历史原因,这个表需要 a、b 做联合主键。

    但是,既然主键包含了 a、b 这两个字段,那意味着单独在字段 c 上创建一个索引,就已经包含了三个字段了呀,为什么要创建“ca”“cb”这两个索引?

    业务中存在这两种查询语句:

    select ... from geek where c=N order by a; 
    select ... from geek where c=N order by b;
    

    问题:为了这两个查询模式,这两个索引是否都是必须的?

    其实这个问题是关于对联合主键索引和 InnoDB 索引组织表的理解。

    表记录:

    abcd
    123d
    132d
    143d
    213d
    222d
    234d

    主键 a,b 的聚簇索引组织顺序相当于 order by a,b ,也就是先按 a 排序,再按 b 排序,c 无 序。

    索引 ca 的组织是先按 c 排序,再按 a 排序,同时记录主键:

    ca主键部分b
    213
    222
    312
    314
    321
    423

    注意,这里主键部分不是 ab,而是只有 b。这个跟索引c的数据是一模一样的。

    索引 cb 的组织是先按 c 排序,在按 b 排序,同时记录主键。

    cb主键部分a
    222
    231
    312
    321
    341
    432

    注意,这里主键部分不是 ab,而是只有 a。

    所以,结论是 ca 可以去掉,cb 需要保留。

    展开全文
  • GIS空间索引

    万次阅读 2020-03-11 12:02:02
    在GIS系统中,空间索引技术就是通过更加有效的组织方式,抽取与空间定位相关的信息组成对原空间数据的索引,以较小的数据量管理大量数据的查询,从而提高空间查询的效率和空间定位的准确性。 常见的GIS空间索引 KD...

    微信搜索:“二十同学” 公众号,欢迎关注一条不一样的成长之路

    在GIS系统中,空间索引技术就是通过更加有效的组织方式,抽取与空间定位相关的信息组成对原空间数据的索引,以较小的数据量管理大量数据的查询,从而提高空间查询的效率和空间定位的准确性。

    常见的GIS空间索引

    1. KD树空间索引(二叉树索引)、KDB树索引
    2. R树、R+树空间索引
    3. G树索引
    4. 四叉树索引及其分类(点四叉树索引、MX四叉树索引、PR四叉树索引、CIF四叉树索引、基于固定网格划分的四叉树索引)
    5. CELL树索引
    6. BSP树空间索引

    1.关于KD树

    在计算机科学里,k-d树(k-维树的缩写)是在k维欧几里德空间组织点的数据结构。k-d树是每个节点都为k维点的二叉树。所有非叶子节点可以视作用一个超平面把空间分区成两部分。在超平面左边的点代表节点的左子树,在超平面右边的点代表节点的右子树。超平面的方向可以用下述方法来选择:每个节点都与k维中垂直于超平面的那一维有关。因此,如果选择按照x轴划分,所有x值小于指定值的节点都会出现在左子树,所有x值大于指定值的节点都会出现在右子树。这样,超平面可以用该x值来确定,其法矢为x轴的单位矢量。(来自维基百科

    GIS中的KD树

    KD树的基本形式存储了K维空间点。KD树的每个内部节点都包含一个点,并且和一个矩形区域相对应。树的根节点和整个研究区域相对应。树中奇数层次上的点的X坐标和偶数层次上的点的Y坐标把矩形区域分成两部分。在KD树结构中,通过沿着树下降到达一个树叶节点的方式来添加一个新点。KD树的查找是从根节点开始,查看所存储的节点(分裂的点)是否被包括在查找范围内以及和左子树或者右子树是否有交叠。对于每个和查询范围交叠的子树,查询过程将重复执行直到到达树叶那一级为止

    示意图如下:

     

    KD树的每个内部结点都包含一个点,每个结点表示k维空间中的一个点,并且和一个矩形区域相对应,树的根结点和整个研究区域相对应。KD树要求用平行于坐标轴的纵横分界线将平面分为若干区域,使每个区域中的点数不超过给定值。树中奇数层次上的点的x坐标和偶数层次上的点的y坐标把矩形区域分成两部分。分界线仅起分界的作用,它的选取没有硬性的限制。一般选用通过某点的横向线或者纵向线。分界线上的点,对左右分界线来说属于右部,对上下分界线来说属于上部。 如图:

    KD树查找

    伪代码

    Algorithm KD_Search(R,P)
    /*在根结点为R的KD树(子树)中查找点P。找到则返回结点,否则返回NULL*/
    Begin
      If R=NULL Then Return NULL;//Not Found
      If R. Point=P Then Return R;//Found;
    Else
      Begin
        d:=Discriminator of R;
        If P[d] <R Point[d] Then /*比较P点与R结点的第D维的值*/
          KD_Search(R.Lchild,P)//在左子树继续查找
        Else
          KD_Search(R.Rchild,P)//在右子树继续查找
      End
    End.

    KD树插入

    伪代码

    Algorithm KD_Insert(R,P,F)
    /*在根结点为R的KD树(子树)中插入点P,F为R的父结点*/
    Begin
      If R=NULL Then
        Begin
          Create a Node P;
          If F=NULL Then //KD树为空
            Root:=P//P成为根结点
          Else
            If R Is the Left child of F Then
              F.Lchild:=P//P作为F的左孩子
            Else F.Rchild:=P//P作为F的右孩子
        End
        Else
          Begin
            d:=Discriminator of R;
            If P[d]R.Point[d] Then/*比较P点与R点的第D维的值*/
              KD_Insert(R.Lchild,P,R)//插入到左子树中
            Else
             KD_Insert(R.Rchild,P,R)// 插入到右子树中
         End;
    End;

    KD树删除

    Algorithm KD_Delete(R,P)
    /*在根结点为R的KD树(子树)中点P,删除成功返回True,否则返回False */
    Begin
          Q:= KD_Search(R,P);//Q为要删除的对象
          LABEL;
          If Q=NULL Then Return False;//Not found
          If (Q.Lchild=NULL) And (Q.Rchild=NULL) Then
            Begin//第一种情况
              F:=Q is Father Node;
              If Q is the left Child of F then
                F.Lchild:=NULL
              Else F.Rchild:NULL;
                Delete Node Q;
              Return True;
           End
        Else
          Begin
             If (Q.Rchild=NULL) Then第三种情况转化为第二种情况处理
                Q.Rchild:=Q.Lchild;
             M:=FindMin(Q.Rchild);
          (Q)←(M);//将M结点的值赋给Q结点
            Q:=M;//让Q指向M结点
            GOTO LABEL;//继续删M结点
        End
    end

    KDB树是KD树与B树的结合,它由两种基本的结构——区域页(region pages,非叶结点)和点页(point pages,叶结点)组成。如图所示

     

    点页存储点目标,区域页存储索引子空间的描述及指向下层页的指针。在KDB树中,区域页则显式地存储了这些子空间信息。区域页的子空间(如s11,S12和s13)两两不相交,且一起构成该区域页的矩形索引空间(如S1)即父区域页的子空间。

    KDB-tree包括两种类型的页:

    • 区域页面: (region, child)对的集合包含边界区域的描述,加上该区域指向子页面的指针。
    • 点页面:(point, location)对的集合。数据库方面,location可能指向数据库记录的索引,对于K维空间中的点,可以被看成该空间中的点坐标。

    当向KDB树插入元素时,导致节点的规模超过它的最优规模,页面溢出。因为KDB-tree的目的是优化外部内存访问,例如硬盘访问,当节点的规模超过外部内存页大小,一个叶被认为是溢出。通过插入和删除操作,KDB树保持一些属性:

    • 该图是一个多叉树,区域页面指向子页面,并且不能为空。点页面是叶子节点。
    • 对于所有查询,到达叶节点的路径长度是相同的。
    • 如果根节点是区域页面,区域的联合是整个搜索空间。
    • 当一个区域页面的(region, child)对的儿子也是一个区域页面,所有儿子区域的联合是该页面。
    • 如果儿子是一个点页面,儿子中所有点必须被该区域包含。

     

    2.关于R树,R+树

    R树是一种多级平衡树,它是B树在多维空间上的扩展。在R树中存放的数据并不是原始数据,而是这些数据的最小边界矩形(MBR),空间对象的MBR被包含于R树的叶结点中。在R树空间索引中,设计一些虚拟的矩形目标,将一些空间位置相近的目标,包含在这个矩形内,这些虚拟的矩形作为空间索引,它含有所包含的空间对象的指针。虚拟矩形还可以进一步细分,即可以再套虚拟矩形形成多级空间索引。

    R树索引是一种高效的空间索引,它是B树在多维空间的扩展,也是平衡树。R树的结构类似于B+树的平衡树。

     

    R树及其特点

    对于一棵M阶的R树,R树中每个非叶子结点都由若干个(p,MBR)数据对组成。MBR(Minimal Boundary Rect)为包含其对应孩子的最小边界矩形。这个最小外接矩形是个广义上的概念,二维上是矩形,三维空间上就是长方体MBV(Minimum Bounding Volume),以此类推到高维空间。p是指向其对应该子结点的指针。

    叶子结点则是由若干个(OI,MBR)组成,其中MBR为包含对应的空间对象的最小外接矩形。OI是空间对象的标号,通过该标号可以得到对应空间对象的详细的信息。

    R树查找

    伪代码如下:

    Algorithm R_Search(N,W) {
        /*在根结点为N的R树中查找所有与W相交的数据矩形*/
    
        if (N.LEVEL==0) //N是叶子结点
            //  Return all data rectangles that intersect with W;
        else //N不是叶子结点
            for (i=1;i<N.COUNT;i++)
                if (N.MBRi;Intersect with W)
                  R_Search (N.pi,W);
    }
    

     

    R树插入

    伪代码如下:

    Algorithm R_Insert(N,P){
    /*向根结点为N的R树中插入数据矩形P*/
      if (N.LEVEL==0) {
            Insert P into N;
            if (N overfill) Split N;
        }
      else {//N是中间结点
          // Choose the entry in N whose rectangle needs 
          // least area enlargement to include the new data rectangle.
          // Resolve ties by choosing the entry with the rectangle of
          // smallest area (Let's suppose it's entry is the answer)
          R_Insert(N.pi,P);
          // Adjust N.MBRi to enclose all rectangle in its child node;
        }
    }
    

    R树删除

    伪代码如下:

    Algorithm R_Delete(N,P){
    /*从根结点为N的R树中删除数据矩形P*/
      if (N:LEVEL==0) 
      {//N是叶结点
          if (N包含P)
          {
              // 从N中删除P
              N.COUNT=N.COUNT-1;
              return true;
          }
          else
              return false;
      }
      else
      {
          for (i =1;i<N.COUNT;i++)
              if (N.MBRi intersects with P)
                if (R_Delete(N.pi,P))
                    if (N.pi,COUNT=m)
                        // Adjust N.MBRi to enclose all child's rectangles;
                    else
                    {
                        // Reinsert all remain entries of N.pi and delete N.pi;
                        // if N underfilled, Reinsert alI         
                        // remain entries of it and
                        // delete it too...;〗
                    }
      }
    }
    

     

    地图对应的R树结构

     

    关于R+树

    在R树的构造中,要求虚拟矩形一般尽可能少地重叠,并且一个空间对通常仅被一个虚拟矩形所包含。但空间对象千姿百态,它们的最小矩形范围经常重叠。 R+ 改进R树的空间索引,为了平衡,它允许虚拟矩形相互重叠,并允许一个空间目标被多个虚拟矩形所包含。

    R+树索引的主要特征是在R+树中兄弟节点对应的空间区域没有重叠,这样划分空间可以使空间搜索的效率提高。R+树也是R树的一个变种,在R+树中,兄弟节点对应的空间区域没有重叠,这样划分空间可以使空间搜索的效率提高。R+树对空间的划分及其索引对象的MBR组织如下:

     

    R+树查找

    算法Search(R,W)/R:R+树的根结点,W:查找矩形窗口/

    S1.[查找中间结点]
    If R是非叶结点 then
      For R的每一索引项(p,MBR) DO
          If MBRW then Search(p,WMBR)
    S2.[查找叶子结点]
    If R是叶子结点 then
      检查R的每一数据项(OI,MBR)
      RETURN所有与W相交的数据矩形
    

    由查找算法可知,与R树相比,对于区域查找,查找路径应该可以减少,但依旧可能有多条;对于点查找,则可以通过一条路径得到查找结果。

    R+树插入

    Algorithm Insert(R,IR){ 
    /*R为R+树的根结点,IR为要插入的数据矩形*/
        I1.[查找中间结点]
        if (R是非叶结点) then
            for (p,MBR) do
              if (MBRIR0) Insert(CHILD,IR);
        I2.[查找叶子结点]
        if (R是叶结点) then
          if (R已有M个数据项)then SplitNode(R);
          else 插入IR于R;
    }
    

     

    R+树删除

    Algorithm Delete (R,IR){ 
    /*R为R+树的根结点,IR为要删除的数据矩形*/
    Dl.[查找中间结点]
    if (R是非叶结点)then
      for R的每一索引项(p,MBR)do
        if (MBRIR0) then Delete(CHILD,IR);
    D2.[查找叶子结点]
    if (R是叶结点) then
      从R中删除IR且调整R的父结点中对应的目录矩形;
    }
    

    结点分裂

    Algorithm SplitNode(R){
    SN1[寻找一个划分]
    调用Partition;
    // 设(p,MBR)为与R相关联的索引项,S1与S2表示划分得到的两个子区域,
    // 创建两个新结点n1=(p1,MBR1)与n2=(p2,MBR2),MBRi=MBRSi,i=1,2;〗
    SN2[填充新结点]
    For (R的每一项(pk,MBRk) do
      if (MBRkMBR==MBRk) then // MBR k完全包含于MBRi
          put(pk,MBRk) in ni;
      else // MBR k与MBR1及MBR2都重叠。
          if (R是叶结点) then
            put (pk,MBRk) in n1 与n2;
          else
            〖用划分线继续分裂(pk,MBRk)所指结点,设得到的新结点为:nk1= 
          (pk1,MBRk1),nk2=(pk2,MBRk2),MBRki完全包含于MBRi,将  
              nki加入到ni,i=I,2;〗
    SN3[向上传播结点分裂操作]
    if (R是根结点)
        创建一新根结点,n1与n2为其两孩子结点;
    else
      // 在R的父结点PR中,用n1与n2替换R。
      // 如果PR的索引项个数超过M,那么调用SplitNode(PR)。
    }
    

    3.G树

    G树是一种多层次的动态生长的格网结构。与KD树类似,G树也按照循环交替的方式分割空间,但是它是采取平均分割空间的方法。假设各维的值,即有关的属性值,都能规范到0到1之间的值,并且每个区域中不能超过2点。如果超过2点,继续循环交替分割空间,直至每个区域不超过2点为止

     

    这种空间分割策略有3个特点:

    1. 区域的二进制编码是全序的;
    2. 分割所得的区域集合构成平面的一个划分;
    3. 区域的二进制编码的位数越多,则该区域越小,它是其编码前缀所代表的区域的子空间

    4.四叉树索引及其分类

    在GIS中,四叉树索引又分为很多种类,包括点四叉树、PR四叉树、MX四叉树、CIF四叉树等

    <1>点四叉树(Point Quadtree)

    点四叉树与KD树相似,两者的差别是在点四叉树中,空间被分割成四个矩形。四个不同的多边形分别是:SW、NW、SE、NE。其搜索过程和KD树相似,当一个点包含在搜索范围内时被记录下来,当一个子树和搜索范围有交叠时它将被穿过。下图:点四叉树示意图

    点四叉树是QuadTree的一个变种,主要是针对空间点的存储表过与索引(Finkel and Bentley,1974),与KD树相似,两者的差别是在点四叉树中,空间被分割成四个矩形,四个不同的多边形对应于SW、NW、SE、NE四个象限。

    对于k维数据空间而言,以新插入的点为中心将其对应索引空间分为两两不相交的2k个子空间,依次与它的2k个孩子结点相对应,对于位于某一子空间的点,则分配给对应的子树。

    点四叉树的每个结点存储了一个空间点的信息及2k个孩子结点的指针,且隐式地与一个索引空间相对应。其搜索过程和KD树相似,当一个点包含在搜索范围内时被记录下来,当一个子树和搜索范围有交叠时它将被穿过。如果想从Point QuadTree中删除一个点的话,则会引起相应的子树的重建,一个简单的方法是将所有子树上的数据重新插入。如图是二维空间的一棵点四叉树的例子。

    优势&劣势

    点四叉树的优点是结构简单,对于精确匹配的点查找性能较高。

    其缺点有:

    1. 树的动态性差,删除结点处理复杂;
    2. 树的结构由点的插入顺序决定,难以保证树深度的平衡;
    3. 区域查找性能较差;
    4. 对于非点状空间目标,必须采用目标近似与空间映射技术,效率较差;
    5. 不利于树的外存存储与页面调度;
    6. 每个结点须存储2k个指针域且其中叶子结点中包含许多空指针,尤其是当k较大时,空间存储开销大,空间利用率低。

    <2>PR四叉树(Point Region Quadtree)

    PR四叉树是点四叉树的一个变种,它不使用数据集中的点来分割空间。在PR四叉树中,每次分割空间时,都是将一个正方形分成四个相等的子正方形,依次进行,直到每个正方形的内容不超过所给定的桶量(比如一个对象)为止。下图:PR四叉树

    PR四叉树是点四叉树的一个变种,它不使用数据集中的点来分割空间。在PR四叉树中,每次分割空间时,都是将一个正方形分成四个相等的子正方形,依次进行,直到每个正方形的内容不超过所给定的桶量(比如一个对象)为止。

    PR四叉树与MX四叉树的主要区别是:

    1. 叶子结点可能不在树的同一层次;
    2. PR四叉树的叶结点数及树的深度都小于MX四叉树,因此PR四叉树的检索效率要高于MX四叉树。

     

    <3>MX四叉树

    空间被分割成四个矩形。四个不同的多边形分别是:SW、NW、SE、NE。每次分割空间时,都是将一个正方形分成四个相等的子正方形,依次进行,直到每个正方形的内容不超过所给定的桶量(比如一个对象)为止。

    所有的数据都处在四叉树的同一个深度,多个点可以由一个指针联接。

    MX四叉树索引即Matrix四叉树索引。在k维空间中,整个数据空间被分割成四个矩形。四个不同的多边形对应于SW、NW、SE、NE四个象限。每次分割空间时,都是将一个正方形分成四个相等的子正方形,依次重复地进行2k次等分,直到每个正方形的内容不超过所给定的桶量(比如一个对象)为止,空间中的每一点都属于某一象限且位于该象限内,每一象限均只与一个空间相关联。

    在MX四叉树中,叶子结点的黑结点或空结点分别表示数据空间某一位置空间点的存在与否。如图所示为二维空间的一棵MX四叉树的例子。

    <4>CIF四叉树

    CIF(Caltech Intermediate From)四叉树是针对表示VLSI(Very Large Scale Integration)应用中的小矩形而提出的,它可以用于索引矩形及其他形体。

    它的组织方式与区域四叉树相似,数据空间被递归地细分直至产生的子象限不再包含任何矩形。在分解的过程中,与任一划分线相交的矩形与该划分线对应的象限相关联,属于一个象限的矩形不能属于祖先象限,换句话说,矩形只属于完全包围它的最小象限。

    下图是二维空间一颗CIF树的例子(这里假设数据桶的容量为3个矩形)。

     

    <5>基于固定网格划分的四叉树索引

    先看下图:

    非叶结点数:MAX_NONLEAFNODE_NUM=∑N−1i=04i∑i=0N−14i

    叶结点数:MAX_LEAFNODE_NUM=2^N×2^N=4N

    非叶结点从四叉树的根结点开始编号:

    从0到MAX_NONLEAFNODE_NUM-1

    叶子结点则从MAX_NONLEAFNODE_NUM开始编号,

    直到MAX_NONLEAFNODE_NUM+MAX_LEAFNODE_NUM-1

    在四叉树中,空间要素标识记录在其外包络矩形所覆盖的每一个叶结点中,但是,当同一父亲的四个兄弟结点都要记录该空间要素标识时,则只将该空间要素标识记录在该父亲结点上,并按这一规则向上层推进。

    在基于固定网格空间划分的四叉树空间索引机制中,二维空间范围被划分为一系列大小相等的棋盘状矩形,即将地理空间的长和宽在X和Y方向上进行2^N等分,形成2^N×2^N的网格,并以此建立N级四叉树。

    在四叉树中,空间要素标识记录在其外包络矩形所覆盖的每一个叶结点中。但当同一父亲的四个兄弟结点都要记录该空间要素标识时,则只将该空间要素标识记录在该父亲结点上,并按这一规则向上层推进。

    把一幅2^n×2^n的图像压缩成线性四叉树的过程

    1. 按Morton码把图象读入一维数组。
    2. 相邻的四个象元比较,一致的合并,只记录第一个象元的Morton码。循环比较所形成的大块,相同的再合并,直到不能合并为止。 
    3. 进一步用游程长度编码压缩。压缩时只记录第一个象元的Morton码。

    解码时,根据Morton码就可知道象元在图像中的位置(左上角),本Morton码和下一个Morton码之差即为象元个数。知道了象元的个数和象元的位置就可恢复出图像了。

    1. 按Morton码读入一维数组。
      Morton码:0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
      象 元 值: A A A B A B B B A A A A B B B B
    2. 四相邻象元合并,只记录第一个象元的Morton码。
      0 1 2 3 4 5 6 7 8 12
      A A A B A A B B A B
    3. 由于不能进一步合并,则用游程长度编码压缩。
      0 3 4 6 8 12
      A B A B A B

    <6>线性可排序四叉树索引

    •首先将四叉树分解为二叉树,即在父结点层与子结点层之间插入一层虚结点,虚结点不用来记录空间要素,然后按照中序遍历树的顺序对结点进行编码,包括加入的虚结点。

    假设某个结点位于四叉树的第N层,可排序四叉树编码为Index。它的四个子结点位于树的第N-1层,编码从左到右分别为:

    Index_C1=Index-3×4×(N-1) 

    Index_C2=Index-4×(N-1)

    Index_C3=Index+4×(N-1)

    Index_C4=Index+3×4×(N-1)

    通过编码值很容易确定结点在树中的层数。在进行查询时,给定一个查询范围,假定为矩形,这个矩形范围唯一的对应一个四叉树结点。通过结点的编码,可以快速计算出在这棵子树下的所有子结点。

    找子结点的范围的程序伪代码如下:

    GetIndexRange(long Index,long Min,long Max)
    {
      long  n = GetLayerNum(Index);
      Min = Max = Index;    
      While(n>0)
      {
        Min = Min- 3×4×(n-1);
        Max = Max-3×4×(n-1);
        n = n –1; 
      }
    }

    5.CELL树索引

    针对R树和R+树在插入、删除与空间搜索效率两个方面难于兼顾的问题,产生了CELL树索引。它在空间划分时不再采用矩形作为划分的基本单位,而是采用凸多边形来作为划分的基本单位,具体划分方法与BSP树有类似之处,子空间不再相互覆盖,如图:

     

    CELL树的磁盘访问次数比R树和R+树少,由于磁盘访问次数是影响空间索引性能的关键指标,因此大大提高了搜索性能,故CELL树是比较优秀的空间索引方法。

    6.BSP树空间索引

    BSP树(Binary Space Partitioning Tree,二值空间划分树)是一种二叉树,它将空间逐级进行一分为二的划分,如下图。BSP树能很好地与空间数据库中空间对象的分布情况相适应,但对一般情况而言,BSP树深度较大,对各种操作均有不利影响,所以在GIS系统中采用BSP空间索引的并不多见。如图:

    BSP的想法最早在Fuchs(1980)中被提出,起初的目的是为了解决实时地消除隐藏面。BSP可以说是八叉树的一般化。前人在这方面已经做了很多有效的工作,Fuchs首次将BSP技术中剖分平面的定侧性质应用于多边形场景的剖分,建立起空间二叉树结构.该二叉树的每一结点表示一个子空间及空间内所包含的多边形。在每一结点空间中,选取其中一平面作为剖分平面,将该空间继续剖分成正负两子空间,分别作为该结点的两个子结点,其中与剖分平面有交的多边形被分割成两个多边形,分别归入相应的子空间中。上述过程是一个递归过程,直至每一子空间仅包含一个多边形为止。与八叉树剖分相比,BSP树具有内存耗费小,剖分方式灵活,产生的无效区域较小的优点;且对大部分场景来说,BSP树较八叉树更为平衡。

     

    生成过程:

    最初,整个区域被定义为 BSP树的根。之后,你继续划分区域。一旦把凹形区域划分为两个凸形区域(在最好情况下)或凹多边形,命名这些区域,它们成为其父结点的孩子,父结点实际上代表了整个区域。

    优缺点

    BSP树能很好地与空间对象的分布情况相适应,但一般而言,BSP树深度较大,对各种操作均有不利影响。 使用BSP树来进行从后向前排序的最大优点就是算法运行的复杂性较低 。这种方法也解决了多边形的多重交叠和多边形穿越问题。但是,通过使用一个预计算结构,我们已经失去了一定的灵活性。如果多边形的排列在运行时发生了改变, BSP树就必须发生相应的改变。

    展开全文
  • 细谈——索引重点内容

    千次阅读 热门讨论 2012-08-06 08:12:55
    一,概念: 百度百科是这样定义的:索引对数据库...有了目录的书,查询其所需看的章节内容来很是迅速、方便。同样数据库中索引的创建加快了数据的查询速度。)   二,索引的创建:  1,直接创建:(1)利用企业管
  • 聚集索引和非聚集索引的区别

    万次阅读 多人点赞 2019-05-19 01:25:35
    微软的SQL SERVER提供了两种索引:聚集索引(clustered index,也称聚类索引、簇集索引)和非聚集索引(nonclustered index,也称非聚类索引、非簇集索引)。下面,我们举例来说明一下聚集索引和非聚集索引的区别: ...
  • B树与红黑树最广泛的应用就是数据库...而索引是在你存储的数据之外,额外保存一些路标(一般是B+树),以减少检索数据的时间。所以索引是主数据衍生的附加结构。 一张表可以建立任意多个索引,每个索引可以是任意多个
  • MySQL 索引概览

    万次阅读 多人点赞 2021-05-25 20:38:15
    文章目录前言概览索引定义索引优缺点优点缺点索引类型按功能逻辑划分普通索引唯一索引主键索引全文索引按物理实现划分聚集索引(clustered index)非聚集索引(non-clustered index)按字段个数划分单一索引组合索引...
  • 数据库索引

    千次阅读 多人点赞 2019-08-20 22:49:54
    文章目录数据库索引定义优缺点索引类型建立普通索引或组合索引适合建立索引的情况索引失效的sql 定义 索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息。数据库索引...
  • 数据库的索引原理 索引作用 索引是用来快速查找特定值的记录。如果没有索引、一般来说执行查询时会遍历整个表。索引就是把无须的数据变成有序的,然后提高查询效率。 索引案例 索引原理 1、把创建了索引的列进行排序...
  • Lucene倒排索引简述 之索引

    千次阅读 2018-09-27 09:57:42
    Lucene倒排索引的核心内容索引表,你对这部分真的熟悉了吗?那你知道FST用什么地方吗?FST又存储了什么内容呢?有什么功能呢?关于Burst-Trie,你知道Lucene是如何采用它的思想来加速Lucene搜索性能的吗?
  • 重建索引的相关内容(from itpub)

    千次阅读 2012-07-31 14:29:22
    一:考虑重建索引的场合1:表上频繁发生update,delete操作2:表上发生了alter table ..move操作(move操作导致了rowid变化)二:判断重建索引的标准 索引重建是否有必要,一般看索引是否倾斜的严重,是否浪费了空间...
  • phoenix索引

    千次阅读 2018-12-16 01:55:52
    1. 介绍 二级索引这个特性应该是大部分用户引入Phoenix主要考虑的因素之一。HBase因其历史原因只支持rowkey索引,当使用...而Phoenix支持除rowkey外的其它字段的索引创建,即二级索引,查询效率可大幅提升。 为什...
  • MySQL索引

    万次阅读 2020-11-25 17:15:44
    索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容索引提供指向存储在表的指定列中的数据值的指针,然后根据您指定的排序顺序对这些指针排序。数据库使用索引以找到特定值,然后顺指针找到...
  • 说到索引,很多人都知道“索引是一个排序的列表,在这个列表中存储着索引的值和包含这个值的数据所在行的物理地址,在数据十分庞大的时候,索引可以大大加快查询的速度,这是因为使用索引后可以不用扫描全表来定位某...
  • 索引3:Hash索引与BitMap索引

    千次阅读 2020-02-11 13:41:49
    Hash索引原理: hash索引是将索引键通过hash运算后,将运算结果的hash值和对应的行指针信息存储Bucket。 引用:‘’哈希索引(hash index)基于哈希表实现,只有精确匹配索引所有列的查询才有效。对于每一行数据,...
  • 数据库的五种索引类型

    万次阅读 多人点赞 2019-03-15 21:24:13
    本文从如何建立mysql索引以及介绍mysql的索引类型,再讲mysql索引的利与弊,以及建立索引时需要注意的地方 首先:先假设有一张表,表的数据有10W条数据,其中有一条数据是nickname='css',如果要拿这条数据的话需要些的...
  • MySQL索引索引优化

    千次阅读 2020-09-04 16:48:00
    MySQL索引就是用于优化器上。 索引: MySQL官方对于索引的定义为:索引是帮助MySQL高效获取数据的数据结构。即可以理解为:索引是数据结构。 索引是对数据库表中一个或多个列的值进行排序的结构,建立索引有助于快速...
  • 1.使用索引调用两个数组中相同位置的内容。brand中index’鸭爪爪’的位置为index[2],在slogan中对应index[2]的内容为‘张大师’,所以输出显示为’最爱吃的就是: 张大师’。 >>> brand = ['火锅','串串',...
  • 索引+索引的类型+创建索引

    千次阅读 2017-05-27 22:42:56
    索引 1在关系数据库中,索引是一种与表有关的数据库结构,它是除了表以外的另一个重要模式对象。 2索引建立在表的一列或多列上...B树索引,反向键索引,位图索引,基于函数的索引,簇索引,全局索引,局部索引等.创建
  • SQL SERVER 索引(3)——聚集索引

    千次阅读 2018-11-08 14:43:02
    聚集索引基于数据行的键值在表内排序和存储这些数据行,对磁盘上实际数据重新组织以按指定的一列或多列值排序,聚集索引的顺序和数据表中数据存储的顺序是一样的。每个表只能有一个聚集索引,因为数据行本身只能按...
  • 当需要存储大量的URL,并且根据URL进行搜索查找,如果使用B+树,存储的内容就会很大 select id from url where url="" 也可以利用将url使用CRC32做哈希,可以使用以下查询方式: select id fom url where url="" and...
  • 索引索引实例分析

    千次阅读 2019-04-18 11:34:42
    一、什么是索引 数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。就像我们以前用的新华字典的目录一样,能帮助我们快速查询到某一个字。 二、索引的分类 分类角度 ...
  • 索引概念

    千次阅读 2016-07-16 12:02:30
    索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容。当表中有大量记录时,若要对表进行查询,第一种搜索信息方式是全表搜索,是将所有记录一一取出,和查询条件进行一一对比,然后返回满足条件的...
  • 非聚簇索引:数据存储和索引分开放,索引结构的叶子节点指向了数据的对应行,myisam通过 key_buffer 把索引先缓存到内存中,当需要访问数据时(通过索引访问数据),在内存中直接搜索索引,然后通过索引找到磁盘...
  • 索引技术

    千次阅读 2014-05-01 21:04:41
    把其中的关于索引内容总结一下,主要是自己做个笔记,以后看着方便。 △ 倒排索引 1 从布尔模型到倒排索引 1.1 怎么出现的倒排索引 最基本的模型是布尔检索模型,但缺点矩阵过于稀疏、结果没有排序。 为了...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 222,155
精华内容 88,862
关键字:

如何减少索引内容