如何减少 iPhone 的 sqlite3 数据库的大小?
-
03-07-2019 - |
题
编辑:非常感谢所有的答案。以下是迄今为止应用优化后的结果:
- 切换到字符排序和游程编码 - 新的 DB 大小 42M
- 删除布尔值上的索引 - 新数据库大小 33M
真正好的部分是这不需要对 iphone 代码进行任何更改
我有一个 iPhone 应用程序,其中有一个以 sqlite 格式保存的大字典(只读)。我正在寻找减少数据库文件大小的想法,该文件目前非常大。
以下是 sqlite 数据库的条目数和结果大小:
franks-macbook:DictionaryMaker frank$ ls -lh dictionary.db
-rw-r--r-- 1 frank staff 59M 8 Oct 23:08 dictionary.db
franks-macbook:DictionaryMaker frank$ wc -l dictionary.txt
453154 dictionary.txt
...每个条目平均大约 135 字节。
这是我的数据库架构:
create table words (word text primary key, sowpods boolean, twl boolean, signature text)
create index sowpods_idx on words(sowpods)
create index twl_idx on words(twl)
create index signature_idx on words(signature)
这是一些示例数据:
photoengrave|1|1|10002011000001210101010000
photoengraved|1|1|10012011000001210101010000
photoengraver|1|1|10002011000001210201010000
photoengravers|1|1|10002011000001210211010000
photoengraves|1|1|10002011000001210111010000
photoengraving|1|1|10001021100002210101010000
最后一个字段表示字谜检索的字母频率(每个位置在 0..9 范围内)。这两个布尔值代表子词典。
我需要进行如下查询:
select signature from words where word = 'foo'
select word from words where signature = '10001021100002210101010000' order by word asc
select word from words where word like 'foo' order by word asc
select word from words where word = 'foo' and (sowpods='1' or twl='1')
我的一个想法是更有效地对字母频率进行编码,例如将它们二进制编码为 blob(也许使用 RLE,因为有很多零?)。关于如何最好地实现这一点的任何想法,或其他减小尺寸的想法?我正在用 ruby 构建数据库,并在目标 C 中在手机上读取它。
还有什么方法可以获取数据库的统计信息,以便我可以看到什么占用了最多的空间?
解决方案
我不清楚签名字段的所有用例,但似乎存储该单词的按字母顺序排列的版本会很有帮助。
其他提示
您是否尝试过输入“vacuum”命令来确保您忘记回收的数据库中没有多余的空间?
删除 sowpods 和 twl 上的索引——它们可能不会帮助您缩短查询时间,而且肯定会占用大量空间。
您可以使用以下方式获取数据库的统计信息 sqlite3_分析器 来自 SQLite 下载页面.
作为一种完全不同的方法,您可以尝试使用 布隆过滤器 而不是综合数据库。基本上,布隆过滤器由一堆哈希函数组成,每个函数都与一个位字段相关联。对于每个合法字,计算每个哈希函数,并设置相应位字段中的相应位。缺点是理论上可能会出现误报,但可以通过足够的哈希值将误报最小化/实际上消除。优点是可以节省大量空间。
SQLite 的创建者销售包含数据库压缩(和加密)的 SQLite 版本。这将是完美的。
最好的选择是使用压缩,不幸的是 SQLite 目前并不支持压缩。幸运的是,有人花时间开发了一个 压缩延伸 这可能就是您所需要的。
否则,我建议主要以压缩格式存储数据并即时解压缩。
作为文本字段, signature
当前每个条目至少使用 26 * 8 字节(208 字节),但如果您要将数据打包到位字段中,则每个字母可能只需要 3 位(将每个字母的最大频率降低到 7)。这意味着您可以将整个签名打包为 26 * 3 位 = 78 位 = 10 字节。即使每个字母使用 4 位(每个字母的最大频率为 15),您也只会使用 104 位(13 个字节)。
编辑:经过更多思考,我认为每个字母 4 位(而不是 3 位)会是一个更好的主意,因为它会使二进制数学更容易。
编辑2:通读文档 SQLite 数据类型, ,似乎您可以使“签名”字段跨越 INTEGER 类型的 26 列,并且 SQLite 将执行正确的操作,并且仅使用存储该值所需的位数。
如前所述,更有效地存储“签名”似乎是一个好主意。
然而,通过使用某种单词查找表,您似乎还可以节省大量空间 - 因为您似乎采用根词,然后附加“er”、“ed”、“es”等,为什么没有一个具有引用来自单独查找表的根单词的数字 ID 的列,以及一个具有引用将附加到基本单词的常用单词后缀表的数字 ID 的单独列。
如果有任何关于使用单个根词存储多个条目的签名的速记版本的技巧,您也可以使用这些技巧来减少存储的签名的大小(不确定是什么算法生成这些值)
这对我来说似乎也很有意义,因为您将“word”列作为主键,但甚至不对其进行索引 - 只需创建一个单独的数字列作为表的主 ID。
嗯……一部 iPhone...它没有永久的数据连接吗?我认为这是 Web 应用程序/Web 服务可以轻松介入的地方。将大部分业务逻辑移至网络服务器(他将拥有真正的带有 FTS 的 SQL 和大量内存),并将该信息在线获取到设备上的客户端。
正如其他地方提到的,丢失布尔列上的索引,它们几乎肯定会比表扫描慢(如果使用的话),并且会不必要地使用空间。
我会考虑对单词进行简单的压缩, 霍夫曼编码 对于这类事情来说非常好。另外,我会看看签名:按字母频率顺序对列进行排序,并且不必费心存储尾随零(这可能是隐含的)。我想你也可以对它们进行霍夫曼编码。
当然,始终假设您的编码字符串不会扰乱 SQLite。