使用大量UPDATE和PostgreSQL进行流式处理

时间:2009-11-02 18:41:31

标签: sql postgresql optimization sql-update

我是PostgreSQL优化的新手,并且选择适当的工作,不管是什么。因此,我想知道每当我尝试使用PostgreSQL进行不适当的工作时,或者它适合它,我应该正确设置所有内容。

无论如何,我需要一个包含大量经常变化的数据的数据库。

例如,假设一个拥有大量客户端的ISP,每个客户端都有一个会话(PPP / VPN /无论如何),有两个自我描述的经常更新的属性bytes_receivedbytes_sent。有一个表格,每个会话由一个具有唯一ID的行表示:

CREATE TABLE sessions(
    id BIGSERIAL NOT NULL,
    username CHARACTER VARYING(32) NOT NULL,
    some_connection_data BYTEA NOT NULL,
    bytes_received BIGINT NOT NULL,
    bytes_sent BIGINT NOT NULL,
    CONSTRAINT sessions_pkey PRIMARY KEY (id)
)

随着会计数据的流动,这个表会收到很多UPDATE,如:

-- There are *lots* of such queries!
UPDATE sessions SET bytes_received = bytes_received + 53554,
                    bytes_sent = bytes_sent + 30676
                WHERE id = 42

当我们收到一个包含很多(如数千个)会话的表的更新(如每秒1-2次)的永无止境的流时,可能感谢MVCC,这使得PostgreSQL 非常很忙。有什么方法可以加速一切,或者Postgres不完全适合这个任务,我最好认为它不适合这个工作,并将这些计数器放到另一个存储器,如memcachedb,使用Postgres仅用于相当静态的数据?但我会错过不经常查询这些数据的能力,例如找到TOP10下载程序,这不是很好。

不幸的是,的数据量不能降低很多。 ISP会计示例全部用于简化说明。真正的问题在于另一个系统,这种结构在某种程度上难以解释。

感谢您的建议!

2 个答案:

答案 0 :(得分:14)

数据库确实不是收集大量小更新的最佳工具,但由于我不知道您的可查询性和ACID要求,因此我无法真正推荐其他内容。如果这是一种可接受的方法,zzzeek建议的应用程序端更新聚合可以帮助显着降低更新负载。

有一种类似的方法可以为您提供耐用性和以某种性能成本查询更新数据的能力。创建一个缓冲表,可以收集需要更新的值的更改并在其中插入更改。在事务中定期将表重命名为其他内容并创建一个新表来代替它。然后在事务中聚合所有更改,对主表执行相应的更新并截断缓冲表。这样,如果您需要从主表中选择的任何数据的一致且全新的快照,并加入来自活动和重命名的缓冲表的所有更改。

但是,如果两者都不可接受,您还可以调整数据库以更好地处理大量更新负载。

要优化更新,请确保PostgreSQL可以使用heap-only tuples来存储更新版本的行。为此,请确保经常更新的列上没有索引,并将fillfactor更改为默认值100%以下的值。您需要自己确定合适的填充因子,因为它很大程度上取决于工作负载及其运行的计算机的详细信息。 fillfactor需要足够低,以至于在autovacuum有机会清理旧的不可见版本之前,所有更新都适合同一数据库页面。您可以调整autovacuum设置以在数据库密度和真空开销之间进行权衡。此外,请考虑任何长事务(包括统计查询)将保留在事务开始后已更改的元组。请参阅 pg_stat_user_tables 视图,了解要调整的内容,尤其是 n_tup_hot_upd n_tup_upd n_live_tup n_dead_tup

大量更新还会产生大量写入日志(WAL)加载。调整WAL行为(docs for the settings)将有助于降低这一点。特别是,更高的checkpoint_segments数和更高的checkpoint_timeout可以通过允许在内存中进行更多更新来显着降低IO负载。请参阅pg_stat_bgwriter中checkpoints_timed与checkpoints_req的关系,以查看有多少检查点发生,因为已达到任一限制。提升你的shared_buffers以使工作集适合内存也会有所帮助。检查buffers_checkpoint与buffers_clean + buffers_backend以查看为满足检查点要求而不是内存耗尽而编写的数量。

答案 1 :(得分:6)

您希望将统计更新组合到某种内存中的队列中,或者如果您更加雄心勃勃,则可以将它们组合到消息总线上。接收过程然后定期聚合这些统计更新 - 可以是从每5秒到每小时的任何地方 - 取决于您想要什么。然后更新bytes_receivedbytes_sent的计数,其中可以表示许多单独的“更新”消息的计数总和在一起。此外,您应该将多个ID的更新语句批处理到单个事务中,确保以与主键相同的相对顺序发出更新语句,以防止对可能执行相同操作的其他事务发生死锁。

通过这种方式,您可以将活动“批处理”到更大的块中,以控制PG数据库上的负载量,并将许多并发活动序列化为单个流(或多个,具体取决于发布更新的线程/进程数) 。根据“周期”调整的权衡是,新增量和更新负载量之间有多少新鲜度。