我正在测试Postgres插入性能。我有一个表,其中一列以数字作为数据类型。它上面也有一个索引。我使用此查询填充数据库:
insert into aNumber (id) values (564),(43536),(34560) ...
我通过上面的查询一次非常快速地插入了400万行。数据库达到600万行后,性能每15分钟急剧下降到100万行。有没有提高插入性能的技巧?我需要在这个项目上获得最佳插入性能。
在具有5 GB RAM的计算机上使用Windows 7 Pro。
请参阅PostgreSQL手册中的populate a database,关于该主题的depesz's excellent-as-usual article和this SO question。
(请注意,这个答案是关于将数据批量加载到现有数据库中或创建新数据。如果您对pg_restore
或psql
执行pg_dump
输出的数据库恢复性能感兴趣,那么大部分内容都不适用,因为pg_dump
和pg_restore
已完成诸如在完成模式+数据恢复后创建触发器和索引之类的事情。
还有很多工作要做。理想的解决方案是导入到没有索引的UNLOGGED
表中,然后将其更改为记录并添加索引。不幸的是,在PostgreSQL 9.4中,不支持将表从UNLOGGED
更改为已记录。 9.5添加ALTER TABLE ... SET LOGGED
以允许您这样做。
如果您可以使数据库脱机以进行批量导入,请使用pg_bulkload
。
除此以外:
COPY
而不是INSERT
sCOPY
考虑使用多值INSERT
s,如果可行的话。你似乎已经这样做了。不要试图在单个VALUES
中列出太多的值;这些值必须在内存中放置几次,所以每个语句保持几百个。synchronous_commit=off
和巨大的commit_delay
来降低fsync()成本。但是,如果你将你的工作分成大型交易,这将无济于事。INSERT
或COPY
从几个连接并行。多少取决于硬件的磁盘子系统;根据经验,如果使用直连存储,则每个物理硬盘驱动器需要一个连接。checkpoint_segments
值并启用log_checkpoints
。查看PostgreSQL日志并确保它不会抱怨检查点发生得太频繁。fsync=off
,启动Pg,执行导入,然后(重要的)停止Pg并再次设置fsync=on
。见WAL configuration。如果在PostgreSQL安装的任何数据库中已经存在任何您关心的数据,请不要这样做。如果你设置fsync=off
你也可以设置full_page_writes=off
;再次,请记住在导入后重新打开它以防止数据库损坏和数据丢失。请参阅Pg手册中的non-durable settings。您还应该考虑调整系统:
fsync()
s的数量 - 它们是不太有益的 - 但仍然可以是一个很大的帮助。除非您不关心保留数据,否则请勿在没有正确电源故障保护的情况下使用廉价的SSD。pg_xlog
)存储在单独的磁盘/磁盘阵列上。在同一磁盘上使用单独的文件系统没什么意义。人们经常选择使用RAID1对来进行WAL。同样,这对具有高提交率的系统有更大的影响,如果您使用未记录的表作为数据加载目标,它几乎没有影响。您可能也对Optimise PostgreSQL for fast testing感兴趣。
根据文档使用COPY table TO ... WITH BINARY
是“somewhat faster than the text and CSV formats”。如果您要插入数百万行,并且您对二进制数据感到满意,则只能执行此操作。
这是一个example recipe in Python, using psycopg2 with binary input。
除了优秀的Craig Ringer的帖子和depesz的博客文章之外,如果你想通过在事务中使用预准备语句插入来加速通过ODBC(psqlodbc)接口的插入,还需要做一些额外的事情来实现它。工作快:
Protocol=-1
,将错误回滚级别设置为“Transaction”。默认情况下,psqlodbc使用“Statement”级别,该级别为每个语句而不是整个事务创建SAVEPOINT,从而使插入更慢。UseServerSidePrepare=1
来使用服务器端预处理语句。如果没有此选项,客户端将发送整个insert语句以及要插入的每一行。SQLSetConnectAttr(conn, SQL_ATTR_AUTOCOMMIT, reinterpret_cast<SQLPOINTER>(SQL_AUTOCOMMIT_OFF), 0);
禁用每个语句的自动提交SQLEndTran(SQL_HANDLE_DBC, conn, SQL_COMMIT);
提交事务。无需明确打开事务。不幸的是,psqlodbc通过发出一系列毫无准备的插入语句来“实现”SQLBulkOperations
,因此为了实现最快的插入,需要手动编写上述步骤。
我今天在同一个问题上花了大约6个小时。插入以“常规”速度(每100K小于3秒)一直到5MI(总共30MI)行,然后性能急剧下降(一直下降到每100K 1min)。
我不会列出所有不起作用的东西,直接切入肉中。
我在主目标表上删除了一个主键(这是一个GUID),我的30MI或行愉快地以每100K不到3秒的恒定速度流到目的地。
为获得最佳插入性能,请禁用索引(如果这是您的选项)。除此之外,更好的硬件(磁盘,内存)也很有帮助
我也遇到了这个插入性能问题。我的解决方案是产生一些例程来完成插入工作。在此期间,SetMaxOpenConns
应该被给予一个正确的数字,否则将提醒太多的开放连接错误。
db, _ := sql.open()
db.SetMaxOpenConns(SOME CONFIG INTEGER NUMBER)
var wg sync.WaitGroup
for _, query := range queries {
wg.Add(1)
go func(msg string) {
defer wg.Done()
_, err := db.Exec(msg)
if err != nil {
fmt.Println(err)
}
}(query)
}
wg.Wait()
我的项目的加载速度要快得多。这段代码只是简单介绍了它是如何工作的。读者应该能够轻松修改它。