介绍了rowid的运用和查询功率,评论了rowid表和无rowid表的优缺点,并对两者的结构和功率进行了比照和总结。

rowid是什么

  在SQLite的数据库中,大多数的表都被称为传统表(也叫rowid表),特色是具有一个仅有的数值键rowid。rowid列是隐含的,假如没有显式定义主键或其他仅有索引,SQLite会主动创立和管理ROWID列。rowid是自增加的64位的integer类型,经过rowid查询会比一般的主键或许索引快两倍。

  下面的句子创立一张Fruits表,SQLite会主动创立一个躲藏的ROWID列:

CREATE TABLE Fruits (name TEXT, color TEXT, price REAL);
INSERT INTO Fruits (name, color, price) VALUES ('Orange', 'Orange', 4);
INSERT INTO Fruits (name, color, price) VALUES ('Apple', 'green', 4.2);
INSERT INTO Fruits (name, color, price) VALUES ('Lemo', 'yellow', 1.2);
INSERT INTO Fruits (name, color, price) VALUES ('Apple', 'red', 7.9);

  经过select * from Fruits; 检查会发现并不能获取rowid这列数据,由于rowid是躲藏列。有必要显式的指定select rowid, * from Fruits 才可以获取rowid列。

SQLite优化之without rowid

  假如为某个单列声明为主键,且该列的数据类型为INTEGER(INTEGER不区分巨细写),则在SQLite内部,将该列名作为一个新的rowid别号,如下fid便是rowid的别号,和rowid是等效的。

CREATE TABLE Fruits2 (fid INTEGER PRIMARY KEY, name TEXT, color TEXT, price REAL);

rowid的效果

  假定在上面创立的Fruits表中有8条数据,杂乱无序的放在数据库中。咱们如何用最快的速度经过指定的生果姓名找到想要的生果呢?咱们仅有能做的便是遍历每一个生果,检查他们的姓名,直到找到了咱们想要的姓名停止。那么在最坏的状况下,咱们就要拜访8条记载,所以此次查找的时刻杂乱度便是O(N)。

  假定可以给每一条数据供给一个数值类型的ID,可以保证ID是顺序排列的。此刻再让咱们经过序号找到指定的生果,咱们就可以经过2分查找法的方法进行查找。在有八条数据的状况下,找到任何一条数据都需求拜访三次。所以像这样去查找的时刻杂乱度便是O(log n) 。很明当数据量较大时O(log n)的查询功率提升很大。rowid便是这样的ID。

SQLite优化之without rowid
SQLite优化之without rowid

rowid表的查询原理

在SQLite数据库中,rowid表本质上的存储结构都是经过B+树来完成的。

1、经过主键rowid查询O(log n)

  B+树是一种多路平衡查找树,它的特色对错叶子节点只存键,叶子节点才存储值。这儿的键指的便是真实的主键,也便是咱们的rowid。在这种多路平衡查找树中,查找数据的时刻杂乱度便是O(log n) 。所以当咱们经过rowid去查询数据的时候,就相当于依据键去直接查找B+树。所以时刻杂乱度也是O(log n)。

2、经过自定义主键查询O(2log n)

//这儿创立的表指定fid(TEXT类型)作为主键,但是rowid才是真实的主键,所以fid其实仅仅一个仅有索引
CREATE TABLE Fruits (fid TEXT PRIMARY KEY, name TEXT, color TEXT, price REAL);
INSERT INTO Fruits (fid, name, color, price) VALUES ('Orange_fid', 'Orange', 'Orange', 4);
select * FROM Fruits WHERE fid = "Orange_fid";

  在SQLite中,索引的完成方法其实便是一般的B树。

  B树同样是一种多路平衡查找树,它的特色便是键和值都存储在所有的节点上。作为索引的话,值指的必定便是要索引的列的内容。由于B树从根结点到叶子结点都可以寄存数据,所以查询B树的时刻杂乱度便是O(log n)。

  所以上述句子经过自定义主键fid查询的状况就被分解成了两步。

1.  首先,假如运用B树找到fid = "Orange_fid" 的rowid
2.  运用rowid 查找B+树找到记载

在经历了两次查找之后,时刻的杂乱度就变成了O(2log n)。

3、经过非主键查询O(2log n)

select * FROM Fruits WHERE name = "Orange";

  假如查询条件既不是rowid又不是主键,像这种状况的话,不管是B树仍是必B树都没有发挥效果,只能经过遍历表中的每一条数据的方法来查询。所以时刻杂乱度就变成了O(n)。不过假如name字段是咱们常常要作为查询条件的字段,最好的方法仍是手动的为这个字段创立一个索引。这样一来,时刻杂乱度就重新的变成了O(2log n)。

  在开发中也会遇到这样一种状况,那便是查找索引之后得到的成果不止一条。那么在这种状况下就要依据索引的成果多次查询rowid表的B+树。这个时候时刻杂乱度就变成了O((X+1)log n),X就等于索引值在表中的重复次数。在大多数的状况下,只要X的值不是太大,所以查询功率必定仍是要高于直接遍历B树。

SQLite优化之without rowid

rowid表的缺点

  1. 只要经过rowid来查询,时刻杂乱度才可以达到最优的O(log n),假如事务上不可以供给数值类型的主键,rowid一般都是经过主动生成和主动增加的方法来完成的,那么事务代码就很难提早拿到rowid并且作为查询条件。
  2. 当咱们用非rowid的主键去查询的时候,时刻杂乱度会变成O(2log n),也便是会慢上一倍。
  3. 每创立一个索引就要额定的存储。大量的记载会额定的糟蹋许多的存储空间。

为了优化这些问题,咱们就可以运用无rowid表。

without rowid表

  without rowid表指的便是咱们经过WITHOUT ROWID关键字创立出来的表,这种表不会主动增加rowid数值键,同时每个without rowid表都有必要声明PRIMARY KEY。如

CREATE TABLE Fruits (fid int PRIMARY KEY,
                     name TEXT, 
                     color TEXT, 
                     price REAL
                    )WITHOUT ROWID;
1、有必要自己指定一个非INTEGER类型的主键或许联合主键。
2、主键的一列有必要不能为空。
3、不可以运用主动增加机制。

WITHOUT ROWID关键字创立出来的表咱们所设置的主键就变成了真实的主键,它的完成原理是聚合索引。无rowid表的存储结构是B Tree,不再是B+Tree。

  由于without rowid表主键的索引不再是简单的仅有性索引,而是真实的聚合索引。也便是说咱们只要可以找到主键,就可以立刻的找到数据。

  由于存储结构从B+Tree变成了B Tree,查询的时刻杂乱度也变成了O (log n)。

  关于rowid表和无rowid表,咱们还可以从结构上做一下比照。其实也便是B Tree和B+Tree的区别。除了咱们刚刚说过的查询时刻杂乱度上的区别,它们之间还有一个区别,便是B Tree的单行数据会影响查询功率。由于数据是在每个节点上都存储的,节点越大查询功率也会变得越低。那么B+Tree就不存在这个问题。由于所有的数据都只在叶子节点上当咱们经过非叶子的节点去进行查询的时候,直到获取到数据之前,单行数据的巨细都不会影响查询功率。

SQLite优化之without rowid

总结

无rowid表的运用场景:

  1、事务可以供给一个非INTEGER类型的主键或许是组合主键,而不是需求数据库来供给主动增加的主键。

  2、无rowid表不适合存储单行数据过大的数据。原因便是单行数据过大的时候,B Tree会影响查询功率。所以无rowid表不适合存储大字符串和BLOBs(二进制大对象)类型。另外一点便是当单行数据不超过1/20数据库页巨细的时候,无rowid表可以获得最佳的功能。这个数据来自SQLite的官方文档。

  即运用无rowid表可以在特定的场景下让主建的查询功率提升一倍,并且可以节省大量的主键的索引相关的字段的存储。

参阅

www.sqlite.net.cn/withoutrowi…

www.sqlite.org/rowidtable.…

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。