最近,一位程序员说,他急需一个“只有”十亿行数据的测试数据库,而且必须在一分钟内生成。于是,他做了所有程序员都会做的事情:编写一个Python脚本来生成数据库。然而不幸的是,这个脚本非常慢。因此,他做了所有程序员都会做的事情:学习更多有关SQLite、Python以及出于某种原因的Rust的知识。项目开源:https://github.com/avinassh/fast-sqlite3-inserts目标作者需要在他的2019款MacBookPro(2.4GHz四核i5)上一分钟生成一个10亿行的SQLite数据库.该表的schema要求:生成的数据是随机的;“地区”栏将包含一个六位数的区号(任何六位数都可以,不需要验证);“年龄”栏是5、10或15A中的任何一个;“active”一栏是0或者1。不过作者说对脚本的要求不需要太高,还是可以妥协的:如果进程崩溃,所有数据都会丢失,没问题,只是再次运行脚本;允许充分利用计算机资源:100%CPU,8GB内存和其余的SSD存储;无需使用真正的随机方法,stdlib中的伪随机方法即可。在Python原型的原始脚本中,作者试图在for循环中将1000万条记录一条一条插入,导致时间直接达到了15分钟。显然,这太慢了。在SQLite中,每一次insert都是一次事务,每一次事务都保证写入磁盘,笔者推测问题可能就出在这里。于是作者开始尝试不同大小的批量插入,发现100000个是最优点,运行时间减少到10分钟。SQLite优化作者认为自己写的代码已经很简洁了,没有优化的余地。于是他将下一个目标转向了数据库的优化。作者根据对SQLite优化的各种建议,做了一些改进。关闭“journal_mode”将禁用回滚日志,即如果任何事务失败,则无法回滚。关闭“synchronous”会让SQLite不再关心它是否能可靠地写入磁盘,而是将这个责任转嫁给操作系统。也就是说,可能存在SQLite无法成功写入磁盘的情况。"cache_size"指定SQLite可以在内存中保留多少内存页。当“locking_mode”为“EXCLUSIVE”模式时,被SQLite锁定的连接永远不会被释放。将“temp_store”设置为“MEMORY”使其表现得像内存数据库。笔者在此提醒,请不要在生产中使用这些操作。RevisitingPython作者再次重写了Python脚本,这次包括微调的SQLite参数,这一次带来了巨大的改进和运行时间的巨大减少:原来的for循环版本大约需要10分钟。批处理版本大约需要8.5分钟。PyPyPyPy在其主页上强调它比CPython快4倍,因此作者决定试一试。令笔者意外的是,无需对现有代码做任何改动,直接在PyPy中运行即可。批处理版本仅需2.5分钟,快了近3.5倍。忙循环?会不会是Python循环花的时间太多了?于是作者删除SQL指令后再次运行代码:批处理版本用CPython耗时5.5分钟。批处理版本在PyPy中需要1.5分钟(另一个3.5倍的加速)。然而,用Rust重写相同的内容后,循环只需要17秒。因此,笔者果断放弃了Python,转而投向了Rust的怀抱。Rust就像Python,作者先写了一个原始的Rust版本,一个循环来执行一行数据的插入。然而,即使使用SQLite的所有优化,它仍然需要大约3分钟。于是作者进行了进一步的测试:尝试将“rusqlite”换成异步运行的“sqlx”,直接将时间拉到了14分钟。根据作者的说法,这比我迄今为止编写的任何Python迭代都要糟糕。执行原始SQL语句时,请使用准备好的语句。这个版本只需要1分钟。最佳版本使用准备语句,以50行为一组插入,耗时34.3秒。作者写了一个线程版本,一个线程从通道接收数据,四个线程向通道推送数据。这也是目前表现最好的版本,最终用时约32.37秒。IO时间SQLite论坛上的一位网友想到了一个有趣的想法:测量一个内存数据库所需的时间。于是作者再次运行代码,将数据库的位置设置为“:memory:”,rust版本少用了两秒(29秒)完成。也就是说,1亿条记录写入磁盘需要2秒,这似乎是合理的。这也说明了一个事实,即可能没有更多的SQLite优化来以更快的方式写入磁盘,因为99%的时间都花在了生成和添加数据上。一亿行数据插入时间排行:Rust33秒PyPy126秒CPython210秒总结尽量使用SQLitePRAGMA语句使用prepared语句批量插入PyPy确实比CPython快4倍异步不一定快目前,第二快的版本是单线程运行的,作者的电脑是4核,所以一分钟可以搞定8亿行数据。那么经过几秒的数据合并,时间仍然可以不到一分钟。网友们对博主的研究发表了评论,获得了网友们的一致好评。真正喜欢的要点:了解了更多关于PRAGMA语句的知识。PyPy的效率和灵活性可以通过即插即用的方式实现(以后一定要给它一个机会)。文章的布局非常简单,带有适当的源代码链接。它很有趣,也很容易上手。Rust高光时刻又来了!
