SQLite处理随机数据慢?预排序让插入性能提升2 - 3倍!

发布时间:2026/7/1 19:21:33
SQLite处理随机数据慢?预排序让插入性能提升2 - 3倍! 随机数据的挑战2026年6月7日安德斯·墨菲探讨了SQLite性能优化问题。在上一篇文章中探讨了 UUID4的随机性如何对插入速度产生重大影响以及UUID7如何解决这一问题。但当面对其他具有随机特性的数据而UUID7又无法解决问题时该怎么办呢将使用由 SecureRandom 生成的160位20字节随机值类似 这篇文章 中描述的情况。对于会话令牌这类数据可能不想使用UUID7因为它会泄露信息且可能无法满足用例对熵的要求等。这也代表任何主键为随机更确切说是无序的数据。以下是生成这些随机值的代码(defn **random-unguessable-uid** [](let [buffer (byte-array 20)](.nextBytes secure-random buffer)))来看看它的性能表现(d/q writer[CREATE TABLE IF NOT EXISTS event(id BLOB PRIMARY KEY, data BLOB) WITHOUT ROWID])(dotimes [_ 10](time(d/with-write-tx [db writer](dotimes [_ 1000000](d/q db [INSERT INTO event (id, data) values (?, ?)(random-unguessable-id) data])))))结果如下总行数时间毫秒10000002478200000049273000000626240000007195500000082576000000870470000009244800000097719000000103871000000011103每秒大约插入十万条记录和使用UUID4一样插入速度很慢。预排序的解决方案B树的显著特点是有序顺序写入速度很快。而随机数据会破坏这一特性导致页面频繁刷新、页面分裂和树的重新平衡影响性能。不过数据已进行了批量处理。那么在插入数据之前对其进行排序会怎样呢首先需要一种快速比较随机ID的方法。这些ID是20字节的不想逐个字节进行比较所以只取前8个字节并将其转换为长整型。在大多数情况下不需要比较整个字节数组就能实现较好的排序效果。重要的是这种比较是无符号的以与SQLite保持一致。当比较两个BLOB值时结果由 memcmp() 函数决定。注意这可能不是对随机数据进行排序的最快方法。写博客时通常不联网联网太容易分心而且现在的搜索功能也不太好用。所以主要依靠离线文档使用 DashLinux下的等效工具是Zeal进行模糊文本搜索。如果知道在Java/Clojure中更快更好的字节数组排序方法请告知以下是相关代码(defn **bytes-long** [^bytes bytes](- (ByteBuffer/wrap bytes 0 8)(ByteBuffer/.getLong 0)))(defn **byte-compare**比较字节数组的前8个最高有效字节。大端字节序与SQLite的BLOB排序一致。[a b](Long/compareUnsigned(bytes-long a)(bytes-long b)))看看排序后的性能表现(d/q writer[CREATE TABLE IF NOT EXISTS event(id BLOB PRIMARY KEY, data BLOB) WITHOUT ROWID])(dotimes [_ 10](time(d/with-write-tx [db writer](- (repeatedly 1000000 random-unguessable-id)(sort byte-compare)(run! (fn [id](d/q db [INSERT INTO event (it, data) values (?, ?) id data])))))))结果如下总行数时间毫秒100000019872000000225130000002296400000026145000000268760000003244700000031188000000331190000003485100000003835很有趣尽管排序会带来一些开销但对批量数据进行排序可以将性能提升约2 - 3倍。结论希望这篇文章能让大家了解到在处理无序数据时对数据进行批量处理并采用预排序等优化方法的好处。完整的基准测试代码可以在 这里 找到。感谢 Datastar Discord 上阅读本文草稿并提供反馈的每一个人。讨论Hacker NewsReddit