SQLite 知识摘要 --- 索引

本篇预备知识

当数据量大的时候,查询操作的效率变得非常重要。对于查询操作来说,如果被查询的数据已某种方式组织起来,那么查询操作的效率会极大提高。

在数据库中,一条记录会有很多列。如果把这些记录按照列Col1以某种数据结构组织起来,那么其他列可能是乱序的。因此,数据库在原始数据之外,维护了满足特定查找算法的数据结构,指向原始数据,称之为索引

举例来说,在下面的图中,数据库有两列Col1、Col2。在存储时,按照列Col1组织各行,比如Col1已二叉树方式组织。如果查找col1中的某一个值,利用二叉树进行二分查找,不需要遍历整个数据库。这样一来列Col2就是乱序的。为了解决这个问题,为Col2建立了索引,即把Col2也按照某种数据结构(这里是二叉树)组织起来。这样子查找列Col2时只需要进行二分查找即可。

image

索引的实现

实现索引引用的数据结构和数据库一样,都是存在本地磁盘的,因此IO是个需要注意的问题。下面简单回顾一下🌲,这种数据结构。

  • 二叉树
    二叉树是一种经典的数据结构,但是并不适合进行数据库索引。因为二叉树中每一个节点的度只有2,树的深度较高。在存储时,一般一个节点需要一次磁盘IO,树的深度较高,查询一个数据需要的磁盘IO次数越高,查找需要的时间越长。

  • B树
    B树是二叉树的变种,主要区别在于每一个节点的度可以大于2,即每一个节点可以分很多叉,大大降低了树的深度。

image
  • 每条数据表示为[key,data]
  • 每个非叶子节点有(n-1)条数据n个指针组成
  • 所有叶节点具有相同的深度,等于树高h
  • 指针指向节点的key大于左边的记录小于右边记录

上面这些特点使得B+树的深度大大降低,并且实现了对数据的有序组织。

  • B+树
    B+树是对B树的扩展,特点在于非叶子节点不存储data,只存储key。如果每一个节点的大小固定(如4k,正如在sqlite中那样),那么可以进一步提高内部节点的度,降低树的深度。
image
  • 非叶子节点只存储key,叶子节点不存储指针
  • 每一个节点大小固定,需要一次读磁盘操作(page)
  • 顺序访问指针的B+树
    对B+树做了一点改变,每一个叶子节点增加一个指向相邻叶子节点的指针,这样子可以提高区间访问的性能。
image
  • 如果没有水平的指针,B+树查找找到key=15的data,在同一个块中找到key=18的data。然后进行第二次B+查找,找到key=20的data,在同一个块中找到key=30的data。

  • 有水平的指针,B+树查找找到key=15的data,查找同一个块的内容,或沿着水平指针依次向右遍历。

Sqlite中数据存储方式

  • 表(table)和索引(Index)都是带顺序访问指针的B+树
  • table对应的B+树中,key是rowid,data是这一行其他列数据(sqlite为每一行分配了一个rowid)
  • index对应的B+树种,key是需要索引的列,data是rowid

根据索引查找数据时,分两步

1 ) 根据索引找到rowid(第一次B+树查找)
2 ) 根据rowid查找其他列的数据(第二次B+树查找)

通过两次B+树查找避免了一次全表扫描。

  1. 对某一行或某几行添加PRIMARY KEY或UNIQUE约束,那么数据库会自动为这些列创建索引
  2. 指定某一列为INTEGER PRIMARY KEY,那么这一列和rowid被指定为同一列。即可以通过rowid来获取,也可以通过列名来获取。

利用索引提高查找效率

比如我们有这么一个表


1 )benchmark
查询语句如下

SELECT price FROM fruitsforsale WHERE fruit=‘Peach’

由于没有索引,因此不得不做一次全表扫描。通过顺序访问指针遍历各个记录(record),比较fruit这一列和‘peatch’是否一致,如果一致,返回这一行的price列的值

2 )对‘fruit’列加索引
如下,运行同样的语句,可以根据索引找到目标列对应的rowid为4,然后根据rowid找到对应行,从而选出price。通过两次B+树查找避免了全表查找。这也是最简单的情况

  1. 多条索引命中
    建立索引时,不要求索引是uique的,即索引表中的key可以是一样的。
    如下图,索引表中有orange两条记录,找到第一条记录时,根据顺序访问指针可以轻易找到下一条索引,避免另一次B+树查找。(rowid=1和rowid=23可能位于两个不同的叶子节点中)
    即这个查找索引的过程,可以通过一次B+树查和一次next操作完成,而next操作是很快的。
  1. 利用索引加快搜索和排序
    在大多情况下,我们需要同时进行查找和排序操作,这时如果建立适当的索引,可以提高查找效率。
    比如下面表中对fruit和state两列做了索引,运行下面的sql语句时,就不需要进行排序操作了,因为索引表是带有顺序的。
SELECT price FROM fruitforsale WHERE fruit='Orange' ORDER BY state

注意detail列。不用索引时,使用的是“SCAN”这个词,即全表扫描。使用索引时,使用的是“SEARCH”这个词。对于一个41G的表来说,进行全表扫描的代价显然是很大的。

优化点:

  • 善用索引

<a name="designindex" id="designindex"></a>

善用索引

索引是提高数据库查询效率非常有效的手段,不过是不是所有的表都适合建索引呢?这是个值得商榷的问题。是不是所有的查询用了索引都比没用索引快?这也是值得商榷的。

什么时候不该使用索引?当一个表上建了索引之后,插入数据的速度会比不建索引的情况慢,因为在插入数据的同时还要修改索引表,有额外的开销。那如果我们在一个不会使用到索引的表上建了索引,自然就成了负担。什么样的表不需要呢?很简单,如果一个表的使用场景基本都是全表查询的(例如只做LIKE查询),那对它建索引并没有什么好处。

如何有效的使用和禁用索引呢?一般情况下,索引都是有效的,但是某些特殊的语句其实并不会使用到索引,对于这些语句,我们可以换一种写法来让索引生效,提高效率。在Sqlite中,LIKE和GLOB都是开销非常大的语句,因为它们并不会使用索引(查询字段建了索引也不会使用),都是全表查询。放着索引不用就是一种浪费,我们可以将LIKE和GLOB换成>、<之类的操作来让索引起作用,例如(x上建了索引):

    x GLOB 'abc*'    替换成    x >= 'abc' AND x < 'abd'
    x LIKE '___'    替换成    length(x) == 3
    x LIKE 'abc_'    替换成    x > 'abc' AND x < 'abd' AND length(x) == 4

替换前后,结果相同,但是前者没有用到索引而后者用到了索引,效率会有些许差别,尤其是在数据量特别大的情况下。

怎样在特殊场景下屏蔽索引?Sqlite中,如果查询使用到索引的话,需要额外加载索引表,先查索引表再查数据表,但是如果某个查询中索引给我们带来的好处小于额外加载索引表的开销,那我们可以想办法绕开索引,例如表的记录数非常小,额外加载索引表查询反而时间更长,或者要查询的记录本来就在表的起始位置附近等。索引优化针对的是"column-name op expression"或者"expression op column-name"(op可以是<、<=、==、=、>=、>)这种格式的语句,我们只需要让条件语句不满足这个格式就可以绕开索引了,例如:

 WHERE x=5    替换成    WHERE x+0=5或者WHERE +x=5

两者结果相同,但是前者使用到了索引,后者没有使用索引,对于某些特殊场景,后者反而比前者快。

并不是所有索引都是好的,需要根据表的实际使用场景,决定是否要创建索引,创建那些字段的索引。在查询的过程中,需要注意有些语句会使用索引,有些语句不会使用索引,我们可以替换不同的写法来控制查询是否使用索引。

参考资料

Query Planning(这篇是sqlite关于索引的文档)

EXPLAIN QUERY PLAN

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,264评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,549评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,389评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,616评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,461评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,351评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,776评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,414评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,722评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,760评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,537评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,381评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,787评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,030评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,304评论 1 252
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,734评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,943评论 2 336

推荐阅读更多精彩内容