【问题标题】:How to reduce the size of an sqlite3 database for iphone?如何减小 iphone 的 sqlite3 数据库的大小?
【发布时间】:2010-09-28 22:11:30
【问题描述】:

编辑:非常感谢所有的答案。以下是到目前为止应用优化后的结果:

  • 切换到对字符进行排序和运行长度编码 - 新的 DB 大小为 42M
  • 删除布尔值上的索引 - 新数据库大小 33M

真正好的部分是这不需要对 iphone 代码进行任何更改

我有一个 iphone 应用程序,其中包含一个以 sqlite 格式(只读)保存的大型字典。我正在寻找减少当前非常大的数据库文件大小的想法。

这里是 sqlite DB 的条目数和结果大小:

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 中通过电话阅读它。

还有什么方法可以获取数据库上的统计信息,以便我可以查看使用最多的空间?

【问题讨论】:

  • 显而易见,但是自从创建/使用数据库以来,您是否对数据库进行了清理?
  • 不,我没有,但是因为我没有删除任何东西(这是使用 ruby​​ 脚本从头开始构建的),所以我认为没有必要。
  • 为了澄清最后一条评论,我只是从一个文本文件(以显示的示例数据的格式)导入,使用:sqlite3 dictionary.db '.import dictionary.txt words
  • 对,所以应该没有碎片,这意味着真空可能没有任何帮助。
  • 对阅读本文的人的重要警告:如果您在 sqlite3 表上使用隐式 rowid,VACUUM 可以(并且将会!)删除您的一些行。

标签: iphone ruby sqlite compression


【解决方案1】:

您是否尝试过键入“vacuum”命令以确保您忘记回收的数据库中没有多余的空间?

【讨论】:

  • 我没有尝试过,但是由于我从不从数据库中删除任何内容(它是从文本文件中一次导入创建的),我不希望它节省任何空间。不过我会试试的。
  • 对阅读本文的人的重要警告:如果您在 sqlite3 表上使用隐式 rowid,VACUUM 可以(并且将会!)删除您的一些行。
【解决方案2】:

删除 sowpods 和 twl 上的索引 - 它们可能不会帮助您缩短查询时间,而且肯定会占用大量空间。

您可以使用来自SQLite downloads pagesqlite3_analyzer 获取数据库的统计信息。

【讨论】:

    【解决方案3】:

    作为一种完全不同的方法,您可以尝试使用bloom filter 而不是综合数据库。基本上,布隆过滤器由一堆散列函数组成,每个散列函数都与一个位域相关联。对于每个合法字,每个散列函数都被评估,并设置相应位域中的相应位。缺点是理论上可能会出现误报,但是可以通过足够的哈希值最小化/实际上消除这些误报。好处是可以节省大量空间。

    【讨论】:

    • 谢谢 - 这是一个非常有趣的想法,对我来说是新的。我将首先追求对字母进行排序和运行长度编码的想法,但也会研究这种方法。后处理步骤可用于消除误报(在这种情况下很容易识别)。
    【解决方案4】:

    我不清楚签名字段的所有用例,但似乎存储单词的字母版本会有所帮助。

    【讨论】:

    • 没错,它可能更小,也适用于字谜搜索(这是唯一的用例)。我会试试的。
    • 好主意!对于这种方法,我能想到的唯一改进是用数字和字母替换连续 3 个或更多字母的序列。例如,如果您对“mississippi”的签名是“iiiimppssss”,您可以将其缩短为“4impp4s”
    • 也是一个好主意...另外,它可能会在根本不更改我当前的 iphone 代码的情况下工作(仍然只是寻找平等)。我也会试试的。计算给定单词的签名的 ruby​​ 代码加分 :-)
    • 请原谅这个解决方案的粗俗,因为我不太了解 Ruby:"mississippi".split( // ).sort.join.gsub(/(.)\1{2,} /) { |s| s.length.to_s + s[0,1] }
    • 我已经设置了另一个问题来进一步探索这个选项。 stackoverflow.com/questions/401834/…
    【解决方案5】:

    SQLite 的创建者销售包含数据库压缩(和加密)的 SQLite 版本。这将是完美的。

    【讨论】:

      【解决方案6】:

      您最好的选择是使用压缩,不幸的是 SQLite 目前不支持本机。幸运的是,有人花时间为它开发了一个compression extension,这可能是您需要的。

      否则,我建议您主要以压缩格式存储数据并即时解压缩。

      【讨论】:

      • 扩展的困难在于将其加载到 iphone 端
      • @frankodwyer:实际上,该扩展调用都可以通过使用简单 SQLite UDF 的任何 SQLite API 完成。
      【解决方案7】:

      作为一个文本字段,signature 当前每个条目至少使用 26 * 8 个字节(208 个字节),但是如果您要将数据打包到一个位字段中,您可能只需要每个字母 3 位(将每个字母的最大频率降低到 7)。这意味着您可以将整个签名打包成 26 * 3 位 = 78 位 = 10 个字节。即使每个字母使用 4 位(每个字母的最大频率为 15),您也只会使用 104 位(13 个字节)。

      编辑:经过深思熟虑,我认为每个字母 4 位(而不是 3 位)会是一个更好的主意,因为它会使二进制数学更容易。

      EDIT2:阅读SQLite data types 上的文档,您似乎可以让“签名”字段跨越 26 个 INTEGER 类型的列,并且 SQLite 会做正确的事情,并且只使用所需的位数存储值。

      【讨论】:

      • 我认为这种方法可以为 OP 节省大量空间。它需要在 SQLite 中使用 BLOB 数据类型。
      • 另外,将 13 个字节四舍五入为 16,这甚至可以拆分为 SQLite 支持的两个 8 字节整数,从而允许在表上使用索引。这种方法需要将签名分成两部分,并且查询需要检查两者是否相等。
      • 是的,我在想一些类似的东西......签名字段中的每个位置都是 0..9,因此可以打包成 4 位。将签名编码为一个大的二进制整数,我认为会再次变小。
      • frankodwyer,我不确定索引 blob。
      【解决方案8】:

      我是否正确地估计您的数据库中有大约 45 万个这样的单词?

      我对 iPhone 一无所知,对 sqlitem 也不认真,但是……只要 sqlite 不允许立即将文件另存为 gz 的方法(它可能已经在内部进行了?不,看起来不像就像你说每个条目大约 135 b。即使没有两个索引),我会远离表格方法,将其“手动”保存在 dictionary approach compression 中,并在运行中和内存中构建其余部分。这应该在您的数据类型上表现得非常好。

      等等...您是否使用该签名来进行全文搜索或误输入识别? sqlite 上的full text search 不会过时该字段吗?

      【讨论】:

      • 是的,大约 450K 字。内存中的方法可能不起作用,因为原始文件数据为 18M 并且手机上的内存非常有限。全文搜索也不起作用,因为我需要运行显示的查询 - 即查找与模式匹配或具有相同签名的所有单词(对于字谜)。
      【解决方案9】:

      如前所述,更有效地存储“签名”似乎是个好主意。

      但是,您似乎也可以通过使用某种单词查找表来节省大量空间 - 因为您似乎要获取一个词根,然后附加“er”、“ed”、“es”等为什么没有一个带有数字 ID 的列引用来自单独查找表的根词,然后有一个带有数字 ID 的单独列引用将附加到基本词的常用词后缀表。

      如果在存储具有单个根词的多个条目的签名的速记版本方面有任何技巧,您还可以使用这些技巧来减少存储的签名的大小(不确定是什么算法产生了这些值)

      这对我来说似乎也很有意义,因为您将“单词”列作为主键,但甚至不对其进行索引 - 只需创建一个单独的数字列作为表的主 ID。

      【讨论】:

      • 听起来很合理,但实现起来相当复杂。
      • 是主键,我知道它是在sqlite中自动索引的
      【解决方案10】:

      嗯……一部 iPhone……它没有永久的数据连接吗? 我认为这是一个 web 应用程序/web 服务可以很好地融入其中的地方。 将您的大部分业务逻辑移至网络服务器(他将拥有真正的 SQL 和 FTS 和 looooots 内存)并在线获取该信息到设备上的客户端。

      【讨论】:

      • 确实如此,但我希望它能够离线工作。而且它只是一个免费的应用程序,我不想为它维护服务器。
      【解决方案11】:

      如其他地方所述,丢失布尔列上的索引,它们几乎肯定会比表扫描慢(如果使用的话)并且会不必要地使用空间。

      我会考虑对单词进行简单的压缩,Huffman coding 非常适合这种事情。另外,我会查看签名:按字母频率顺序对列进行排序,不要费心存储尾随零,这可能是暗示的。我猜你也可以对这些进行霍夫曼编码。

      当然,始终假设您的编码字符串不会扰乱 SQLite。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多