我喜欢PostgreSQL防崩溃,因为我不想花时间修复数据库。但是,我确信必须有一些我可以禁用/修改的内容,这样即使在电源中断/崩溃之前我丢失了几条记录,插入/更新也会更快地运行。我并不担心几条记录 - 只是整个数据库。
我正在尝试优化PostgreSQL以进行大量写入。插入100万行目前需要22分钟,这看起来有点慢。
如何加快PostgreSQL写入速度?
我调查过的一些选项(比如full_page_writes),似乎也存在破坏数据的风险,这不是我想要的。我不介意丢失数据 - 我只是不想要腐败。
这是我正在使用的表 - 这是因为大多数表都包含整数和小字符串,这个“样本”表似乎是我应该期待的最好的例子。
CREATE TABLE "user"
(
id serial NOT NULL,
username character varying(40),
email character varying(70),
website character varying(100),
created integer,
CONSTRAINT user_pkey PRIMARY KEY (id)
)
WITH ( OIDS=FALSE );
CREATE INDEX id ON "user" USING btree (id);
我有大约10个脚本,每个脚本使用预准备语句一次发出100,000个请求。这是为了模拟我的应用程序将为数据库提供的实际负载。在我的应用程序中,每个页面都有1个以上的插入。
我正在使用异步提交,因为我有
synchronous_commit = off
在主配置文件中。
答案 0 :(得分:57)
在22分钟内插入的1M记录可达到758条记录/秒。这里的每个INSERT都是对磁盘的单独提交,最终具有预写日志和数据库组件。通常情况下,我希望即使是具有电池备份缓存的优质硬件,您也将幸运地达到3000次/秒。所以如果这是没有这种写入加速的常规硬件,那么你实际上并没有做得太糟糕。在这种情况下,正常限制在500到1000次提交/秒范围内,没有针对这种情况进行特殊调整。
至于那些看起来如何,如果你不能让提交包括更多的记录,你加快这一点的选择包括:
关闭synchronous_commit(已经 完成)
增加wal_writer_delay。什么时候 synchronous_commit是关闭的, 数据库假脱机承诺 每200ms写一次。你(们)能做到 而是相当于几秒钟 如果你想通过调整这个 向上,它只是增加了大小 崩溃后数据丢失。
将wal_buffers增加到16MB,只是为了 使整个操作更多 高效。
增加checkpoint_segments,剪切 关于常规数据的频率 写入磁盘。你可能想要 这里至少有64个。缺点是磁盘空间使用更多,恢复时间更长 崩溃后。
增加shared_buffers。默认 这里很小,通常为32MB。您 必须增加UNIX共享的数量 系统必须分配的内存。 一旦完成,有用的值就是 通常>总RAM的1/4,最高可达 8GB。这里的收益率下降了 超过256MB,增加了 默认可以有 虽然很有帮助。
这就是它。你触摸的任何其他可能有帮助的东西都可能导致崩溃中的数据损坏;这些都是完全安全的。
答案 1 :(得分:4)
100万行的22分钟似乎 慢,特别是如果你有很多索引。
你是如何进行插入的?我认为你使用批量插入,而不是每行一行。
PG是否支持某种批量加载,例如从文本文件读取或向其提供CSV数据流?如果是这样,你最好建议你使用它。
请发布您用于加载1M记录的代码,人们会建议。
请发帖:
编辑:OP似乎对批量插入不感兴趣,但是正在对许多单行插入进行性能测试。我将假设每个插入都在自己的事务中。
答案 2 :(得分:3)
嗯,你不能给我们太多的东西继续下去。但听起来你正在寻找asynchronous commits。
不要忽视硬件升级 - 更快的硬件通常意味着更快的数据库。
答案 3 :(得分:3)
我认为仅通过处理服务器无法解决问题。
我发现PostgreSQL每秒可以提交3000多行,服务器和客户端都不忙,但时间过去了。相比之下,SQL Server每秒可以达到5000多行,而Oracle甚至更快,它可以达到每秒12000+,连续约20个字段。
我想往返是问题:向服务器发送一行,并从服务器接收回复。 SQL Server和Oracle都支持批处理操作:在函数调用中发送多行并等待回复。
多年前我曾与Oracle合作:尝试使用OCI提高写入性能,我阅读文档并发现过多的往返会降低性能。最后,我通过使用批处理操作解决了它:批量发送128行或更多行到服务器并等待回复。它达到每秒12000多行。如果你不使用批处理并单独发送所有行(包括等待),它每秒只能达到大约2000行。
答案 4 :(得分:2)
你也应该增加checkpoint_segments
(例如增加到32或更高),最有可能增加wal_buffers
修改强>
如果这是批量加载,则应使用COPY插入行。它比普通的INSERT快得多。
如果需要使用INSERT,您是否考虑使用批处理(对于JDBC)或多行插入?
答案 5 :(得分:2)
1M 提交在22分钟内似乎是合理的,即使使用def processTweet(tweet):
tweet = tweet.lower()
tweet = re.sub('((www\.[^\s]+)|(https?://[^\s]+))','URL',tweet)
tweet = re.sub('@[^\s]+','USER',tweet)
tweet = re.sub('[\s]+', ' ', tweet)
tweet = re.sub(r'#([^\s]+)', r'\1', tweet)
tweet = tweet.translate(None, string.punctuation)
tweet = tweet.strip('\'"')
return tweet
for filename in glob.glob(os.path.join(path, '*.txt')):
with open(filename) as file:
tweet=file.read()
processedTweet = processTweet(tweet)
file = open(filename, "w")
file.write(processedTweet)
file.close()
,但是如果你可以避免在每个插入上提交,那么你可以比这快得多。我只是尝试使用bulk-insert synchronous_commit = off
命令从10个并发编写器中将1M(相同)行插入到示例表中:
COPY
显然,那里只有10个提交,这并不是您正在寻找的,但是希望通过批量插入可以为您提供某种可能的速度指示一起。这是在一个运行Linux的VirtualBox VM上,在一个相当标准的Windows桌面主机上运行,因此不是最高性能的硬件。
为了减少一些玩具数字,我们有一个在生产中运行的服务,它有一个单独的线程,通过类似于上面的$ head -n3 users.txt | cat -A # the rest of the file is just this another 99997 times
Random J. User^Irjuser@email.com^Ihttp://example.org^I100$
Random J. User^Irjuser@email.com^Ihttp://example.org^I100$
Random J. User^Irjuser@email.com^Ihttp://example.org^I100$
$ wc -l users.txt
100000 users.txt
$ time (seq 10 | xargs --max-procs=10 -n 1 bash -c "cat users.txt | psql insertspeed -c 'COPY \"user\" (username, email, website, created) FROM STDIN WITH (FORMAT text);'")
real 0m10.589s
user 0m0.281s
sys 0m0.285s
$ psql insertspeed -Antc 'SELECT count(*) FROM "user"'
1000000
命令将数据传输到Postgres。它结束批处理并在一定数量的行之后提交,或者如果事务达到某个年龄(以先到者为准)。它每秒可以承受11,000次插入,最大延迟为~300ms,每秒执行~4次提交。如果我们收紧了交易的最大允许年龄,我们每秒可以获得更多的提交,这将减少延迟,但也会减少吞吐量。同样,这不是非常令人印象深刻的硬件。
根据这些经验,我强烈建议您尝试使用COPY
而不是COPY
,并尝试尽可能减少提交次数,同时仍然实现延迟目标。
答案 6 :(得分:1)
你可以做的一件事就是放慢手动创建的索引 - primary key
约束已自动在该列上创建一个唯一索引,如下所示(我正在测试8.3):
postgres=> CREATE TABLE "user"
postgres-> (
postgres(> id serial NOT NULL,
postgres(> username character varying(40),
postgres(> email character varying(70),
postgres(> website character varying(100),
postgres(> created integer,
postgres(> CONSTRAINT user_pkey PRIMARY KEY (id)
postgres(> )
postgres-> WITH ( OIDS=FALSE );
NOTICE: CREATE TABLE will create implicit sequence "user_id_seq" for serial column "user.id"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "user_pkey" for table "user"
CREATE TABLE
postgres=> CREATE INDEX id ON "user" USING btree (id);
CREATE INDEX
postgres=> \d user
Table "stack.user"
Column | Type | Modifiers
----------+------------------------+---------------------------------------------------
id | integer | not null default nextval('user_id_seq'::regclass)
username | character varying(40) |
email | character varying(70) |
website | character varying(100) |
created | integer |
Indexes:
"user_pkey" PRIMARY KEY, btree (id)
"id" btree (id)
另外,请考虑将wal_sync_method
更改为使用O_DIRECT
- this is not the default on Linux
答案 7 :(得分:0)
一种可能性是使用按键DEFERRABLE延迟约束,因为每行都检查约束。
所以这个想法是在提交之前让postgresql检查约束。