以可能的数据丢失为代价提高PostgreSQL的写入速度?

时间:2011-02-27 03:32:55

标签: database postgresql

我喜欢PostgreSQL防崩溃,因为我不想花时间修复数据库。但是,我确信必须有一些我可以禁用/修改的内容,这样即使在电源中断/崩溃之前我丢失了几条记录,插入/更新也会更快地运行。我并不担心几条记录 - 只是整个数据库。

我正在尝试优化PostgreSQL以进行大量写入。插入100万行目前需要22分钟,这看起来有点慢。

如何加快PostgreSQL写入速度?

我调查过的一些选项(比如full_page_writes),似乎也存在破坏数据的风险,这不是我想要的。我不介意丢失数据 - 我只是不想要腐败。

更新1

这是我正在使用的表 - 这是因为大多数表都包含整数和小字符串,这个“样本”表似乎是我应该期待的最好的例子。

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个以上的插入。

更新2

我正在使用异步提交,因为我有

  

synchronous_commit = off

在主配置文件中。

8 个答案:

答案 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记录的代码,人们会建议。

请发帖:

  • 您正在加载的表的CREATE TABLE语句
  • 您用于加载的代码
  • 数据的小例子(如果可能)

编辑:OP似乎对批量插入不感兴趣,但是正在对许多单行插入进行性能测试。我将假设每个插入都在自己的事务中。

  • 考虑在客户端,每个节点上对插入进行批处理,将它们写入临时文件(希望持久/健壮),并使用守护程序或某些定期进程,以异常方式对合理大小的未完成记录进行批量插入批次。
  • 根据我的经验,这种每设备批处理机制确实提供了最佳性能,例如数据仓库应用程序等数据仓库应用程序,其中数据不需要直接进入数据库 。它还为应用程序提供了对数据库不可用的弹性。
  • 当然,您通常会有几个端点设备创建审计记录(例如,电话交换机,邮件中继,Web应用程序服务器),每个设备都必须有自己的完全独立的机制实例。
  • 这是一个非常“聪明”的优化,它在应用程序设计中引入了很多复杂性,并且有很多地方可能会发生错误。除非你确定你需要它,否则不要实现它。

答案 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检查约束。