几天前,我遇到了一个意想不到的性能问题,使用了非常标准的Django设置。对于即将推出的功能,我们必须每小时重新生成一个表,包含大约10万行数据,磁盘上9M,根据pgAdmin的10M索引。
问题是,通过任何方法插入它们需要很长时间,最多3分钟的100%磁盘繁忙时间。这不是你想要的生产网站。如果插入是在事务中,通过普通插入,多行插入,COPY FROM或甚至INSERT INTO t1 SELECT * FROM t2发出也无关紧要。
注意到这不是Django的错,我遵循了试错路线,嘿,丢掉所有外键后问题就消失了! INSERT INTO SELECT FROM执行时间不到3分钟,这对于磁盘上的表< = 20M来说并不太令人惊讶。什么 奇怪的是,PostgreSQL只使用3个外键就可以减少插入速度180倍。
哦,磁盘活动是纯粹的写作,因为所有内容都缓存在RAM中;只写入磁盘。看起来PostgreSQL正在努力触摸引用表中的每一行,因为3MB / sec * 180s的数据量比这个新表占用磁盘的20MB多。对于180年代的情况没有WAL,我在Django中直接在psql中测试,为WAL日志记录增加了约50%的开销。试过@commit_on_success,同样慢,我甚至用psycopg2实现了多行插入和COPY FROM。这是另一个奇怪的事情,10M价值的插入物怎么能产生> 10x 16M日志段?
表格布局:id serial primary,一堆int32,3个外键
所以,我注定要手动删除外键,或者通过定义保存bla_id x3并跳过使用models.ForeignKey,以非Django的方式使用表。我很想知道一些神奇的解药/ pg设置来解决这个问题。
答案 0 :(得分:2)
如果不必等待IO读取,则100,000次FK检查大约需要2-5秒。 比插入表格慢得多,但比你得到的时间快得多。
检查所有外键是否为INDEXED:
(我在谈论引用列的索引,而不是引用列,得到它了吗?)
如果products.category_id参考类别(id),并且category.id上没有索引,则每次需要检查FK时,都必须扫描该表。
要查找哪个不是,请使用1 FK插入,然后使用2个FK ...您将找到哪个负责。
是的,如果你截断表,那么删除所有约束和索引并在批量插入后重建它们的速度会更快。
答案 1 :(得分:0)
这对我来说似乎是正常行为。批量插入数据库时,如果表具有索引,外键或触发器,则必须逐行检查它们。所以通常你想删除它们,执行插入(如果可能的话,使用copy),然后重新创建索引,FK和触发器。
文档上的此页面提供了有关您可以调整的autocommit,maintenance_work_mem和checkpoint_segments的更多详细信息:http://www.postgresql.org/docs/8.4/interactive/populate.html
答案 2 :(得分:0)
也许你的桌子上有一个触发器,你不知道或记住,每次插入/删除的行都会触发。你能用“psql”连接数据库吗?如果是,则分析所有表的“\ d + table_name”的输出。
您还可以转储数据库,导入,再次转储数据库。比较转储以检查是否有任何其他表内容已更改。
答案 3 :(得分:0)
我忘记了EXPLAIN ANALYZE INSERT INTO bleh ...会告诉你所有插入触发器的时间。