tur*_*nip 19 python postgresql sql-insert postgresql-copy
今天,我花了一天时间来改进我的Python脚本的性能,该脚本将数据推送到我的Postgres数据库中.我以前是这样插入记录的:
query = "INSERT INTO my_table (a,b,c ... ) VALUES (%s, %s, %s ...)";
for d in data:
cursor.execute(query, d)
Run Code Online (Sandbox Code Playgroud)
然后我重新编写了我的脚本,以便创建一个内存文件,而不是用于Postgres的COPY命令,它允许我将数据从文件复制到我的表:
f = StringIO(my_tsv_string)
cursor.copy_expert("COPY my_table FROM STDIN WITH CSV DELIMITER AS E'\t' ENCODING 'utf-8' QUOTE E'\b' NULL ''", f)
Run Code Online (Sandbox Code Playgroud)
这种COPY方法速度惊人.
METHOD | TIME (secs) | # RECORDS
=======================================
COPY_FROM | 92.998 | 48339
INSERT | 1011.931 | 48377
Run Code Online (Sandbox Code Playgroud)
但我找不到任何关于为什么的信息?它与多线程的工作方式有何不同INSERT,使其更快?
也看到这个基准:
# original
0.008857011795043945: query_builder_insert
0.0029380321502685547: copy_from_insert
# 10 records
0.00867605209350586: query_builder_insert
0.003248929977416992: copy_from_insert
# 10k records
0.041108131408691406: query_builder_insert
0.010066032409667969: copy_from_insert
# 1M records
3.464181900024414: query_builder_insert
0.47070908546447754: copy_from_insert
# 10M records
38.96936798095703: query_builder_insert
5.955034017562866: copy_from_insert
Run Code Online (Sandbox Code Playgroud)
Cra*_*ger 17
这里有许多因素在起作用:
COMMIT 成本,如果每个插入一次提交的人(你不是)COPY- 批量加载的特定优化如果服务器是远程的,您可能正在"支付"每个语句的固定时间"价格",例如50ms(1/20秒).或者对于某些云托管DB更多.由于下一个插入在最后一个插入成功完成之前无法开始,这意味着您的最大插入速率为1000 /往返 - 延迟 - 以毫秒为单位的每秒行数.延迟为50毫秒("ping时间"),即20行/秒.即使在本地服务器上,此延迟也是非零的.Wheras COPY只填充TCP发送和接收窗口,并按照DB可以写入的速度流式传输行,网络可以传输它们.它不受延迟的影响很大,并且可能在同一网络链路上每秒插入数千行.
在PostgreSQL中解析,规划和执行语句也有成本.它必须采取锁定,打开关系文件,查找索引等COPY尝试在开始时执行所有这一次,然后只关注尽可能快地加载行.
由于操作系统必须在您的应用程序准备并发送它之前在等待连续的postgres之间切换,然后您的应用程序在postgres处理该行时等待postgres的响应,因此需要支付更多时间成本.每次从一个切换到另一个,都会浪费一点时间.进程进入和离开等待状态时,可能会浪费更多时间来暂停和恢复各种低级内核状态.
最重要的是,COPY它可以用于某些类型的负载.如果没有生成的键,并且任何默认值都是常量,例如,它可以预先计算它们并完全绕过执行程序,将数据快速加载到较低级别的表中,完全跳过PostgreSQL正常工作的一部分.如果您CREATE TABLE或TRUNCATE在同一个事务中COPY,它可以通过绕过多客户端数据库中所需的正常事务簿保存来更快地实现负载.
尽管如此,PostgreSQL COPY仍然可以做更多的事情来加快速度,它还不知道该怎么做.它可以自动跳过索引更新,然后重建索引,如果您正在更改超过表的某个比例.它可以批量进行索引更新.还有更多.
最后要考虑的是提交成本.对你来说这可能不是问题,因为psycopg2默认是打开一个事务而不是在你告诉之前提交.除非你告诉它使用autocommit.但对于许多DB驱动程序,autocommit是默认设置.在这种情况下,你将为每一个做一次提交INSERT.这意味着一次磁盘刷新,服务器确保将内存中的所有数据写入磁盘并告诉磁盘将自己的缓存写入持久存储.这可能需要很长时间,并且会因硬件而有很大差异.我的基于SSD的NVMe BTRFS笔记本电脑只能达到200 fsyncs /秒,而300,000非同步写入/秒.所以它只能加载200行/秒!有些服务器只能执行50 fsyncs /秒.有些可以做到20,000.因此,如果您必须定期提交,请尝试批量加载和提交,执行多行插入等.因为最后COPY只提交一个,所以提交成本可以忽略不计.但这也意味着COPY无法通过数据中途从错误中恢复; 它解除了整个批量负荷.
复制使用批量加载,这意味着它每次插入多行,而简单插入,一次插入一次,但是您可以按照语法插入多行:
insert into table_name (column1, .., columnn) values (val1, ..valn), ..., (val1, ..valn)
Run Code Online (Sandbox Code Playgroud)
有关使用批量加载的更多信息,请参阅Daniel Westermann 在 postgresql 中加载 1m 行的最快方法。
一次应该插入多少行的问题取决于行的长度,一个好的经验法则是每个插入语句插入 100 行。
| 归档时间: |
|
| 查看次数: |
5423 次 |
| 最近记录: |