SQLite 中的各种限制 已翻译 100%

鱼米三香 投递于 2016/08/30 16:28 (共 8 段, 翻译完成于 09-05)
阅读 15594
收藏 78
4
加载中

在这篇文章的环境里,“限制”意味着不能超过设定的大小或者数量。我们关心的是诸如在BLOB或者表中最大字节数。

SQLite最初被设计成避免任何限制策略的数据库。当然,运行任意程序的机器都有特定大小内存和磁盘空间限制。但是在SQLite,哪些限制没有被定义。该策略是,如果有足够内存并且可以运行在32位机器上,那么它应该可以工作。

不幸的是,没有限制策略已经暴露出问题了。因为上限没有定义,它们没经过(极限)测试,当把SQLite推向极限时,漏洞(包括潜在的安全隐患)经常出现。鉴于此原因,SQLite的新版本明确定义了限制,并且那些限制作为单元测试的一部分。

xufuji456
翻译于 2016/08/30 19:24
0

本文定义了 SQLite 的限制,如何针对这些限制定制特定的应用程序。默认的限制设置通常是适当的,几乎适合于每一个应用。有一些应用程序可能需要在这里或者那里增加一个设置,但是我们估计这非常罕见。更普遍的是,应用程序可能需要重新编译SQLite以及更低的限制来避免过多的资源利用率,以及在高级SQL语句生成器上帮助阻止攻击者注入恶意SQL语句时发生错误。

在使用 sqlite3_limit() 接口的 limit categories 上,为该接口定义一些限制,可以在运行时改变每个基础的连接。应用程序设计的运行时限制多数据库,一些仅供内部使用的限制可以影响或控制潜在的敌对外部代理。举例来说,一个web浏览器应用程序可能使用一个内部的数据库来追踪历史页面浏览量,但是它有一个或很多分离的数据库,它们被创建和控制是通过 javascript 应用,这些应用都是从互联网上下载的。那么 sqlite3_limit() 接口是允许通过可信代码来管理约束内置数据库的,同时在数据库创建或控制上有着严格的限制,它会拒绝不可信的外部代码攻击服务。



溪边九节
翻译于 2016/08/31 20:35
0

1、string或者BLOB的最大长度

SQLite中string或者BLOB的最大字节数是由预处理器宏SQLITE_MAX_LENGTH定义的。这个宏的默认值是10亿,你可以在编译时使用像下面这样的命令行参数来对这个默认值进行调整:

-DSQLITE_MAX_LENGTH=123456789

在当前实现中仅支持将string或者BLOB长度上调到最大231-1 or 2147483647。并且这个时候一些内置的函数例如hex()将会调用失败。在安全敏感的应用中最好不要尝试增加string和BlOB的最大长度。实际上,如果可以的话,你可以将string和BLOB的最大长度在一定范围内降低(几百方字节)。

在SQLite的INSERT和SELECT处理时,数据库中中的每一行的所有内容都被编码成单个BLOB。所以SQLITE_MAX_LENGTH这个参数同样也定义了一行的最大字节数。string或者BLOB的最大长度可以在运行时通过sqlite3_limit(db,SQLITE_LIMIT_LENGTH,size) 方法调低。

2、最大列数

SQLITE_MAX_COLUMN在编译时用来设置一个上限:默认设置SQLITE_MAX_COLUMN的值是2000。你可以在编译的时候将它调整到最大32767。另一方面,许多经验丰富的数据库设计者会认为一个设计良好的数据库永远都不会在表中需要超过100列。

在大多数应用中的列数是很小的,大约几十个而已。在SQLite代码生成器中使用的算法是O(N²),这个N就是列数。所以如果你重新定义SQLITE_MAX_COLUMN为一个巨大的数字,那么在生成SQL的时候使用这个大列数你就会发现sqlite3_prepare_v2() 运行的很慢。最大列数可以在运行时使用sqlite3_limit(db,SQLITE_LIMIT_COLUMN,size) 方法调低。

  • 表中的列数

  • 索引中的列数

  • 视图中的列数

  • UPDATE语句中SET子句中的项数

  • SELECT语句结果集中的列数

  • GROUP BY 或者 ORDER BY子句中的项数

  • INSERT语句中的value项数

气质舞王尼古拉斯赵四
翻译于 2016/09/02 17:23
1
  • SQL语句的最大长度

    一个SQL语句文本中字节数的最大值,受限于 SQLITE_MAX_SQL_LENGTH ,其默认为 1000000。你可以重新对这个限制进行定义,大到 SQLITE_MAX_LENGTH 和 1073741824 两者中较小的一个值。

    如果一个SQL语句在长度上被限制在1百万个字节以内,那么很明显你就不能够以字符串字面量的形式将几百万字节嵌入到 INSERT 语句中。不过你应该是不会那样做的。这时候针对这些数据你可以使用占位参数,像下面这样先准备好一个简短的SQL语句:

    INSERT INTO tab1 VALUES(?,?,?);

    然后使用 sqlite3 的_bind_XXXX() 函数来将大型的字符串值绑定到这个SQL语句。绑定的使用回避掉了要在字符串中对引号进行转义的必要, 同时降低了遭受SQL注入攻击的风险。它运行起来也更快,因为大型的字符串不必进行其它方式要进行的多次转换和复制操作。

    SQL语句的最大长度可以在运行时使用 sqlite3 的_limit(db,SQLITE_LIMIT_SQL_LENGTH,size) 接口来减小。

  • 一次连接操作中最大的表数量

    SQLite 不支持超过64个表的连接操作。此限制源于在查询优化器中,SQLite代码生成器会使用每个连接表一个位的位图,这一事实。

    SQLite 使用了一种高效的 查询规划器算法 ,因此即使是一个大型地连接操作也能被快速地被 预处理好。 所以没有任何机制来提高或者降低一次连接操作中表的数量。


  • 表达式树结构的最大深度

    SQLite 会将表达解析成一个数结构来进行处理。在代码生成期间,SQLite会以递归的形式遍历这个树结构。表达式树结构的深度因此被加上了限制,以此避免用掉太多的栈空间。

    SQLITE_MAX_EXPR_DEPTH 参数决定了表达式树结构的最大深度。如果其值为 0,就表示不加任何限制。目前的实现所使用的默认值是 1000。

    如果 SQLITE_MAX_EXPR_DEPTH 初始是正数,那么表达书树结构的最大深度可以在运行时使用 sqlite3 的_limit(db,SQLITE_LIMIT_EXPR_DEPTH,size) 接口来降低。换言之,如果已经在编译时对表达式深度做了限制,那么表达式树结构深度的最大值就可以在运行时被降低。如果If SQLITE_MAX_EXPR_DEPTH 的值在编译时被设置为 0(即表达式的深度不受限制),那么sqlite3 的_limit(db,SQLITE_LIMIT_EXPR_DEPTH,size) 接口就是一个无效操作。

LeoXu
翻译于 2016/09/03 12:34
1
  • 函数里参数的最大数量

    SQLITE_MAX_FUNCTION_ARG 参数决定了能够被传入到一个SQL函数的参数的最大数量。这一限制的默认值为100。SQLite 应该在函拥有数以千计的参数时也能运作。不过,我们对于那些使用过多参数的人持怀疑态度,因为他们可能是在尝试找出使用了SQLite的系统中的安全漏洞,而不是做一些实用的事情, 而因为这个原有,我们已经给这个参数设置了相对而言较低的值。

    传入函数的参数数量有时会被存储在一个有符号的字符中,因此 SQLITE_MAX_FUNCTION_ARG 有一个限定死的 127 的上限。

    一个函数中的参数的最大数量可以在运行时使用 sqlite3 的_limit(db,SQLITE_LIMIT_FUNCTION_ARG,size) 接口来降低。


  • 一个复合SELECT语句中段落的最大数量

    一个复合 SELECT 语句就是那种由操作符 UNION, UNION ALL, EXCEPT, 或者 INTERSECT 连接起来的两个或者更多个SQL语句。我们将一个复合SELECT中的每一个独立的SELECT语句称为一个“段落”。

    SQLite中的代码生成器会使用一种递归算法来对复合SELECT语句进行处理。对栈的大小有必要进行一下限制,我们会因为这个缘故对复合SELECT中的段落数量进行限制。段落的最大数量限制参数就是 SQLITE_MAX_COMPOUND_SELECT ,其默认值为 500。我们认为这已经是一个比较宽裕的分配方案了,因为在实际使用中很少会遇到一个复合SELECT中段落的数量超过个位数。

    复合SELECT段落的最大数量在运行时可以使用 sqlite3 的_limit(db,SQLITE_LIMIT_COMPOUND_SELECT,size) 接口来降低。


  • LIKE 或者 GLOB 模式的最大数量

    在特定的一些极端场景中,SQLite默认的LIKEGLOB实现中所使用的模式匹配算法会表现出 O(N²) 的性能消耗(这里的N指的是模式中字符的数量。为了避免遭受来自那些能够自己指定 LIKE 或者 GLOB 模式的人所进行的拒绝服务攻击, LIKE 或者 GLOB 模式的长度被 SQLITE_MAX_LIKE_PATTERN_LENGTH 所指定的位数值进行了限定。这个的默认值为 50000。现代的工作站能够以相对较快的速度计算出一个拥有50000位这样极端长度的 LIKE 或者 GLOB 模式。拒绝服务攻击的问题只有在模式长度达到百万个字节位数这样的程度时才会造成影响。不过因为大多数实用的 LIKE 或者 GLOB模式长度大多只有几十个字节,偏执的开发者如果知道外部用户拥有生成任意模式的能力,也许就会想要把这个参数限制到几百个这样的范围之内。 

    LIKE 或者 GLOB 模式的最大长度可以在运行时使用 sqlite3 的_limit(db,SQLITE_LIMIT_LIKE_PATTERN_LENGTH,size) 接口来减小。


LeoXu
翻译于 2016/09/03 15:44
0
  • 单个SQL语句中预置参数的最大个数

    预置参数就是SQL语句中的一个占位符,将来会被使用sqlite3 其中的一个_bind_XXXX() 接口填充。许多SQL程序员对于使用问号 ("?") 作为预置参数都很熟悉。SQLite 也支持以前置 ":", "$", 或者 "@"的命名作为预置参数,并且以“?123”这样的形式对预置参数进行编号。

    SQLite语句中的每一个预置参数都被分配了一个数字。该数字一般以1开始,而后对于每一个新的参数其数字加一。然而,当“?123”这样的形式被使用了的时候,预置参数的编号就会是问号后面跟着的那个数字。

    SQLite 会给从1到最大预置参数数量之间的预置参数分配空间。因此,一个包含了像 ?1000000000 这样的预置参数的SQL就会需要千兆字节的存储。这样就会很容易地超过主机的资源供应能力。为了防止这种过度了内存分配,预置参数的最大数量就要限制到 SQLITE_MAX_VARIABLE_NUMBER 这样一个固定值,其默认为 999。

    预置参数的最大数量可以在运行时使用 sqlite3 的_limit(db,SQLITE_LIMIT_VARIABLE_NUMBER,size) 接口来减小。


  • 触发器递归的最大深度

    SQLite 限制了触发器的递归深度,一次来阻止一个涉及到递归触发器的语句毫无限度的使用内存。

    版本 3.6.18之前的SQLite,触发器并非递归的,因此这一限制毫无意义。从版本3.6.18开始,递归触发器得到了支持,但必须使用 PRAGMA recursive_triggers 语句来明确声明启用了它。从版本3.7.0开始,递归触发器就是默认被启用了的,但可以使用 PRAGMA recursive_triggers 来手动禁用。SQLITE_MAX_TRIGGER_DEPTH 只在其启用时有效。

    触发器递归默认的最大深度为1000。

LeoXu
翻译于 2016/09/03 16:06
0

从属数据库的最大数量

ATTACH 语句是一项SQLite扩展,它能让两个或者更多个数据库与同一个数据库连接联合起来,操作起来就好像它们是一个数据库。能够被联合的数据库数量被限制到了 SQLITE_MAX_ATTACHED 这样一个常量,其默认值为10。被联合数据库的最大数量不能超过125。

被联合的数据库的最大数量可以在运行时使用 sqlite3_limit(db,SQLITE_LIMIT_ATTACHED,size) 接口来减小。

一个数据库文件中页面的最大数量

SQLite 能够限制数据库文件的大小,以阻止数据库文件变得太大而消耗过多的磁盘空间。SQLITE_MAX_PAGE_COUNT 参数,一般会被设置成 1073741823, 就是一个数据库文件中页面的最大数量。一次会造成数据库文件增长超过这个值的插入新数据的操作将会返回 SQLITE_FULL。

 SQLITE_MAX_PAGE_COUNT 最大的可能设置是 214748364。当最大页面大小为 65536 时, 最大的SQLite数据库大小就大概是140太字节。最大页面总数PRAGMA 可以被用来在运行时提高或者降低这个限制。

表中数据行的最大数量

表中数据行的最大数量理论上可以到 264 (18446744073709551616 or about 1.8e+19)。这一限制是不可及的,因为数据库会首先达到140太字节的大小限制。一个140太字节大小的数据库可以容纳超过将近 1e+13 行的数据,而要容纳这么多行数据,只能在没有指数数据存在,并且每一行只包含非常少量数据的时候。

LeoXu
翻译于 2016/09/03 16:37
0

最大数据库大小

每个数据库都包含一个或多个“页”(page)。在某个单一数据库中,每个页的大小相同,但是不同的数据库拥有不同的页大小,大小介于512字节(含)和65536字节(含)之间。一个数据库文件最多包括2147483646页。每个页最大65536字节,换算可得最大数据库大小约为1.4e+14字节(140千万兆字节或128太字节或140000百万兆字节或128000吉字节)。由于开发人员没有能满足这个限制的硬件条件,所以没有对这个上限进行测试。然而,测试表明当数据库达到底层文件系统的最大文件大小(通常要比理论上的最大数据库大小小很多)并且由于磁盘空间耗尽而无法扩展时,SQLite依然能正确并稳健地运行。

模式中表的最大数量

数据库文件中的每个表和索引至少需要一个页。这里的索引可以使用CREATE INDEX 语句显式地创建,也可以通过UNIQUE和PRIMARY KEY约束隐式地创建。由于数据库文件的页的最大数量是2147483646(比20亿还多),所以模式中表和索引的数量上限也是这个。

只要数据库被打开,就会扫描和解析整个模式,并将模式的解析树存储在内存中。也就是说数据库的启动时间和初始内存使用率与模式大小成正比。

昌伟兄
翻译于 2016/08/31 21:47
1
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(22)

HYUO
HYUO

引用来自“ylxs90”的评论

我就想知道,sqlite现在支持事务了吗?

引用来自“eechen”的评论

SQLite一直都支持事务.你应该问微软: Access支持事务了么?什么时候开源免费跨平台呀. 哈哈.
https://msdn.microsoft.com/en-us/library/bb208950(v=office.12).aspx 无知并不是ee神你的错
ddatsh
ddatsh
用H2吧
归园田居
归园田居
喜闻乐见
eechen
eechen

引用来自“ylxs90”的评论

我就想知道,sqlite现在支持事务了吗?
SQLite一直都支持事务.你应该问微软: Access支持事务了么?什么时候开源免费跨平台呀. 哈哈.
eechen
eechen
绝大部分限制(并发写/网络)是有意设计的,它们是SQLite设计的结果.
例如,支持高度的写并发会带来很大的复杂性,这将使SQLite的简单性无法保持.
同样,作为嵌入式数据库,SQLite是有意不支持网络的,这一点并不奇怪.
总之,SQLite不能做的都是它所能做的直接结果.
它开始的设计就是一款模块化,简单,紧凑的以及易于使用的嵌入式关系数据库,
它的基础代码都在使用它的程序员的掌握范围内.
从多方面看,它能完成许多其他数据库不能做的事情,
例如,运行在对功耗敏感的嵌入式环境中.

eechen
eechen
另外SQLite还支持全文检索:
CREATE TABLE t1_real(id INTEGER PRIMARY KEY, a, b, c, d);
CREATE VIRTUAL TABLE t1_fts USING fts4(content="t1_real", b, c);
删除数据时,先删fts表,再删real表:
DELETE FROM t1_fts WHERE rowid = 123;
DELETE FROM t1_real WHERE rowid = 123;
或者用触发器来自动更新fts(Full Text Search)表:
CREATE TRIGGER t2_bu BEFORE UPDATE ON t2 BEGIN
  DELETE FROM t3 WHERE docid=old.rowid;
END;
CREATE TRIGGER t2_bd BEFORE DELETE ON t2 BEGIN
  DELETE FROM t3 WHERE docid=old.rowid;
END;
CREATE TRIGGER t2_au AFTER UPDATE ON t2 BEGIN
  INSERT INTO t3(docid, b, c) VALUES(new.rowid, new.b, new.c);
END;
CREATE TRIGGER t2_ai AFTER INSERT ON t2 BEGIN
  INSERT INTO t3(docid, b, c) VALUES(new.rowid, new.b, new.c);
END;
eechen
eechen
SQLite数据库实例信息保存在数据库文件里的系统表sqlite_master:
临时表不会出现在 sqlite_master 表中.
临时表及其索引和触发器存放在另外一个叫 sqlite_temp_master 的表中.
表中有自增字段时,SQLite数据库会新建一个sqlite_sequence表来记录表名name和当前序号seq.
SELECT * FROM sqlite_sequence

使用"解释查询计划"查看索引使用情况:
EXPLAIN QUERY PLAN SELECT * FROM queue WHERE id = 1
SEARCH TABLE queue USING INTEGER PRIMARY KEY (rowid=?)
没有索引则显示:SCAN TABLE queue(全表扫描)

使用EXPLAIN查看SQLite虚拟数据库引擎VDBE(The Virtual Database Engine of SQLite)运行SQL时的信息:
http://www.sqlite.org/vdbe.html
EXPLAIN SELECT * FROM sqlite_master
addr opcode p1 p2 p3 p4 p5 comment
13 TableLock 0 1 0 sqlite_master 00
每条指令(instruction)对应1个操作码(opcode)和5个操作数(operand).
When the VDBE completes execution,
all open database cursors are closed,
all memory is freed, and everything is popped from the stack.
So there are never any worries about memory leaks or undeallocated resources.
当VDBE执行完成后,所有打开的数据库游标都被关闭,所有分配的内存都会被释放,
栈中的每个东西都被弹出,因此不需要担心内存泄漏或未回收的资源问题.
eechen
eechen
SQLite图形化管理工具可以用波兰程序员基于Qt开发的免费开源跨平台的SQLiteStudio.
为Linux/OSX/Windows提供有解压即用的二进制包:
http://sqlitestudio.pl/files/sqlitestudio3/complete/

@卖萌的程序猿 SQLite 3.7.0之前不支持写的时候读.SQLite 3.7.0开始(PHP-7.0.0自带SQLite-3.8.10.2),对并发控制做了优化,提供了WAL(write-ahead log)预写式日志模式,支持一个写和多个读并发,但同一个时刻仍然只能有一个写事务.
PRAGMA journal_mode; 返回 delete
PRAGMA journal_mode = WAL; 启用 WAL(相关文件:data.db3-wal,data.db3-shm)
你可以将SQLite数据库置于Linux内存文件系统/dev/shm/data.db3,这样读写将不受制于磁盘性能.

执行 PRAGMA synchronous 可见默认值是2(即FULL).
PRAGMA synchronous = FULL
PRAGMA synchronous = OFF
关闭同步时,写操作不用等数据真正写到磁盘上才返回,可实现异步写.
运行SQLite的程序(比如PHP-FPM进程)崩溃,数据库文件不会损坏,但在系统崩溃或写入数据时意外断电的情况下则可能会损坏.开启WAL的时候,官方建议选择NORMAL的同步模式:
PRAGMA synchronous = NORMAL;
PRAGMA journal_mode = WAL;

SQLite事务隔离级别(isolation):
PRAGMA read_uncommitted; 返回0,表示隔离级别为最严格的串行化.
SQLite只支持两种隔离级别,串行化和读未提交.
串行化在事务开启时上读锁,在事务提交或回滚时释放锁.
读未提交,就是读全程不上锁.

在SQLite中,ALTER TABLE命令允许用户重命名表,或向现有表添加一个新的列.
重命名列,删除一列,或从一个表中添加或删除约束都是不支持的.
码农-厨子
码农-厨子
这货的全表锁怎么处理?
苍耳道人
苍耳道人

引用来自“灰灰”的评论

SQLite,几百K的程序,就可以实现大部分SQL语句,而且很多语言集成了。比Access这样的玩具好多了,Access以前测试时,连嵌套SELECT都不支持,不知道现在行不行?
从2003就支持啊,若你用的是更新语句,很多数据库都不支持,不止access。
返回顶部
顶部