Small. Fast. Reliable.
Choose any three.

SQLite中的限制

本文上下文中的“限制”表示不能超过的大小或数量。我们关心的是诸如BLOB中最大字节数或表中最大列数之类的问题。

SQLite最初的设计宗旨是避免任意限制。当然,在内存和磁盘空间有限的计算机上运行的每个程序都有某种限制。但是在SQLite中,这些限制没有得到很好的定义。策略是,如果它适合内存并且可以用32位整数进行计数,那么它应该可以工作。

不幸的是,无限制政策已经显示出会产生问题。由于上限没有很好地定义,因此未对其进行测试,并且在将SQLite推向极限时经常发现错误。因此,自3.5.8(2008-04-16)发行版以来的SQLite版本具有明确定义的限制,并且这些限制已作为测试套件的一部分进行了测试

本文定义了SQLite的局限性以及如何针对特定应用程序对其进行自定义。限制的默认设置通常很大,几乎适用于所有应用程序。某些应用程序可能希望在此处或此处增加限制,但是我们希望这种需求很少见。更常见的是,应用程序可能希望以较低的限制重新编译SQLite,以避免在高级SQL语句生成器中发生错误时避免过多的资源利用,或帮助阻止注入恶意SQL语句的攻击者。

某些限制可以在运行时使用sqlite3_limit()接口在每个连接的基础上进行更改,该接口具有为该接口定义的限制类别之一 。运行时限制是为具有多个数据库的应用程序设计的,其中一些数据库仅供内部使用,而其他数据库可能会受到潜在敌对外部代理的影响或控制。例如,Web浏览器应用程序可能使用内部数据库来跟踪历史页面浏览量,但具有一个或多个单独的数据库,这些数据库由从互联网上下载的javascript应用程序创建和控制。该sqlite3_limit() 接口允许不受约束的代码管理的内部数据库不受约束,同时对不受信任的外部代码创建或控制的数据库设置严格的限制,以帮助防止拒绝服务攻击。

  1. 字符串或BLOB的最大长度

    SQLite中字符串或BLOB中的最大字节数由预处理程序宏SQLITE_MAX_LENGTH定义。该宏的默认值为10亿(10亿或1,000,000,000)。您可以使用以下命令行选项在编译时提高或降低该值:

    -DSQLITE_MAX_LENGTH = 123456789

    当前实现仅支持最大2 31 -1或2147483647的字符串或BLOB长度。某些内置函数(例如hex())可能在此之前就失败了。在对安全性敏感的应用程序中,最好不要尝试增加最大字符串和blob长度。实际上,如果可能的话,最好将最大字符串长度和blob长度减小到几百万。

    在SQLite的INSERT和SELECT处理的一部分期间,数据库中每一行的全部内容被编码为单个BLOB。因此,SQLITE_MAX_LENGTH参数还确定一行中的最大字节数。

    可以使用sqlite3_limit(db,SQLITE_LIMIT_LENGTH,size)接口在运行时降低最大字符串或BLOB长度。

  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)接口可以在运行时降低最大列数。

  3. SQL语句的最大长度

    SQL语句文本中的最大字节数限制为SQLITE_MAX_SQL_LENGTH,默认值为1,000,000,000。

    如果一个SQL语句的长度限制为一百万个字节,那么很显然,您无法通过将它们作为文字嵌入到INSERT语句中来插入数百万个字节的字符串。但是您无论如何都不应该这样做。 对数据使用主机参数。准备像这样的简短SQL语句:

    插入tab1值(?,?,?);

    然后,使用sqlite3_bind_XXXX()函数将较大的字符串值绑定到SQL语句。绑定的使用避免了对字符串中的引号字符进行转义的需要,从而降低了SQL注入攻击的风险。由于不需要分析或复制大字符串,因此它的运行速度也更快。

    可以在运行时使用sqlite3_limit(db,SQLITE_LIMIT_SQL_LENGTH,size)接口降低SQL语句的最大长度。

  4. 联接中的最大表数

    SQLite不支持包含超过64个表的联接。此限制是由于SQLite代码生成器使用位图而在查询优化器中每个连接表使用一位的事实引起的。

    SQLite使用高效的查询计划程序算法 ,因此甚至可以快速准备大型联接。因此,没有机制可以提高或降低联接中表数量的限制。

  5. 表达式树的最大深度

    SQLite将表达式解析到树中进行处理。在代码生成期间,SQLite递归地遍历此树。因此,表达式树的深度受到限制,以避免使用过多的堆栈空间。

    SQLITE_MAX_EXPR_DEPTH参数确定最大表达式树深度。如果该值为0,则不强制执行任何限制。当前实现的默认值为1000。

    如果SQLITE_MAX_EXPR_DEPTH最初为正, 则可以在运行时使用sqlite3_limit(db,SQLITE_LIMIT_EXPR_DEPTH,size)接口降低表达式树的最大深度。换句话说,如果表达式深度已经存在编译时限制,则可以在运行时降低最大表达式深度。如果在编译时将SQLITE_MAX_EXPR_DEPTH设置为0(如果表达式的深度不受限制),则sqlite3_limit(db,SQLITE_LIMIT_EXPR_DEPTH的大小)是无操作的。

  6. 一个函数的最大参数个数

    SQLITE_MAX_FUNCTION_ARG参数确定可以传递给SQL函数的最大参数数量。此限制的默认值为100。SQLite应该与具有数千个参数的函数一起使用。但是,我们怀疑任何试图使用多个参数调用函数的人实际上都在试图在使用SQLite的系统中发现安全漏洞,而没有做有用的工作,因此出于这个原因,我们将该参数设置得相对较低。

    函数参数的数量有时存储在带符号的字符中。因此,SQLITE_MAX_FUNCTION_ARG的上限为127。

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

  7. 复合SELECT语句中的最大术语数

    复合SELECT语句是通过运算符UNION,UNION ALL,EXCEPT或INTERSECT连接的两个或多个SELECT语句。我们将复合SELECT中的每个单独的SELECT语句称为“项”。

    SQLite中的代码生成器使用递归算法处理复合SELECT语句。为了限制堆栈的大小,因此我们限制了复合SELECT中的项数。术语的最大数量为SQLITE_MAX_COMPOUND_SELECT,默认值为500。我们认为这是一个慷慨的分配,因为在实践中,我们几乎从来没有看到复合选择中的术语数量超过个位数。

    可以在运行时使用sqlite3_limit(db,SQLITE_LIMIT_COMPOUND_SELECT,size)接口降低复合SELECT术语的最大数量。

  8. LIKE或GLOB模式的最大长度

    默认LIKEGLOB中使用的模式匹配算法 对于某些病理情况,SQLite的实现可以表现出O(N²)性能(其中N是模式中的字符数)。为了避免能够指定自己的LIKE或GLOB模式的恶意者进行的拒绝服务攻击,LIKE或GLOB模式的长度限制为SQLITE_MAX_LIKE_PATTERN_LENGTH字节。该限制的默认值为50000。现代工作站甚至可以相对快速地评估甚至是50000字节的病理LIKE或GLOB模式。拒绝服务问题仅在模式长度达到数百万个字节时才起作用。不过,由于最有用的LIKE或GLOB模式的长度最多为几十个字节,

    可以使用sqlite3_limit(db,SQLITE_LIMIT_LIKE_PATTERN_LENGTH,size)接口在运行时降低LIKE或GLOB模式的最大长度。

  9. 单个SQL语句中的最大主机参数数

    主机参数是使用sqlite3_bind_XXXX()接口之一填充的SQL语句中的占位符 。许多SQL程序员熟悉使用问号(“?”)作为主机参数。SQLite还支持以“:”,“ $”或“ @”开头的命名主机参数以及形式为“?123”的带编号主机参数。

    SQLite语句中的每个主机参数都分配有一个数字。数字通常以1开头,每个新参数增加1。但是,使用“?123”形式时,主机参数编号是问号后的编号。

    SQLite分配空间以容纳所有主机参数(介于1和所使用的最大主机参数号之间)。因此,一条包含?1000000000之类的主机参数的SQL语句将需要GB的存储空间。这很容易使主机资源不堪重负。为防止过多的内存分配,主机参数号的最大值为SQLITE_MAX_VARIABLE_NUMBER,对于3.32.0(2020-05-22)之前的SQLite版本,其默认值为999;对于3.32.0之后的SQLite版本,其默认值为32766。

    可以在运行时使用sqlite3_limit(db,SQLITE_LIMIT_VARIABLE_NUMBER,size)接口降低主机参数的最大数量。

  10. 触发递归的最大深度

    SQLite限制了触发器的递归深度,以防止涉及递归触发器的语句使用无限制的内存量。

    在SQLite 3.6.18(2009-09-11)之前,触发器不是递归的,因此此限制毫无意义。从版本3.6.18开始,支持递归触发器,但必须使用PRAGMA recursive_triggers语句显式启用它 。从版本3.7.0(2009-09-11)开始,默认情况下启用了递归触发器,但可以使用PRAGMA recursive_triggers手动禁用它。仅当启用了递归触发器时,SQLITE_MAX_TRIGGER_DEPTH才有意义。

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

  11. 最大连接数据库数

    ATTACH语句是一个SQLite扩展,它允许两个或更多的数据库要关联到同一个数据库连接,并进行操作,就好像它们是一个单一的数据库。同时连接的数据库数限制为SQLITE_MAX_ATTACHED,默认情况下设置为10。附加数据库的最大数量不能增加到125以上。

    使用sqlite3_limit(db,SQLITE_LIMIT_ATTACHED,size)接口可以在运行时降低附加数据库的最大数量。

  12. 数据库文件中的最大页数

    SQLite能够限制数据库文件的大小,以防止数据库文件变得太大而占用过多的磁盘空间。SQLITE_MAX_PAGE_COUNT参数通常设置为1073741823,它是单个数据库文件中允许的最大页数。尝试插入新数据将导致数据库文件大于该数据的尝试将返回SQLITE_FULL。

    SQLITE_MAX_PAGE_COUNT的最大可能设置为4294967294。与最大页面大小为65536一起使用时,这将使最大SQLite数据库大小约为281 TB。

    max_page_count PRAGMA可以用来提高或降低在运行时此限制。

  13. 表中的最大行数

    表格中理论上的最大行数是2 64(18446744073709551616或大约1.8e + 19)。由于将首先达到281 TB的最大数据库大小,因此无法达到此限制。281 TB的数据库最多可以容纳2e + 13行,然后只有在没有索引并且每行包含的数据很少的情况下才可以。

  14. 最大数据库大小

    每个数据库都包含一个或多个“页面”。在单个数据库中,每个页面的大小都是相同的,但是不同的数据库可以具有的页面大小是512到65536(含)之间的2的幂。数据库文件的最大大小为4294967294页。在最大页面大小为65536字节的情况下,这将转换为最大数据库大小约为1.4e + 14字节(281 TB或256 TB,或281474 GB或256,000 GI)。

    由于开发人员无法访问能够达到此限制的硬件,因此未测试此特定上限。但是,当数据库达到基础文件系统的最大文件大小(通常比理论上的最大数据库大小小得多)时,并且由于磁盘空间耗尽而导致数据库无法增长时,测试确实可以验证SQLite的行为是否正确,合理。

  15. 架构中的最大表数

    每个表和索引在数据库文件中至少需要一页。前一句中的“索引”表示使用CREATE INDEX语句显式创建的索引或由UNIQUE和PRIMARY KEY约束创建的隐式索引。由于数据库文件中的最大页面数为2147483646(略高于20亿),因此这也是架构中表和索引数的上限。

    每当打开数据库时,都会扫描并解析整个架构,并将架构的解析树保存在内存中。这意味着数据库连接启动时间和初始内存使用量与架构的大小成正比。