【问题标题】:postgreSQL get last ID in partitioned tables /postgreSQL 获取分区表中的最后一个 ID /
【发布时间】:2013-06-04 11:58:14
【问题描述】:

我的问题与这个问题基本相同,但我找不到答案,它还写了“待在下一个版本中解决”和“易于最小/最大扫描”

PostgreSQL+table partitioning: inefficient max() and min()

CREATE TABLE mc_handstats
(
  id integer NOT NULL DEFAULT nextval('mc_handst_id_seq'::regclass),
  playerid integer NOT NULL,
  CONSTRAINT mc_handst_pkey PRIMARY KEY (id),
);

表是根据 playerid 分区的。

CREATE TABLE mc_handst_0000 ( CHECK ( playerid >= 0 AND playerid < 10000) ) INHERITS (mc_handst) TABLESPACE ssd01;
CREATE TABLE mc_handst_0010 ( CHECK ( playerid >= 10000 AND playerid < 30000) ) INHERITS (mc_handst) TABLESPACE ssd02;
CREATE TABLE mc_handst_0030 ( CHECK ( playerid >= 30000 AND playerid < 50000) ) INHERITS (mc_handst) TABLESPACE ssd03;
...

CREATE INDEX mc_handst_0000_PlayerID ON mc_handst_0000 (playerid);
CREATE INDEX mc_handst_0010_PlayerID ON mc_handst_0010 (playerid);
CREATE INDEX mc_handst_0030_PlayerID ON mc_handst_0030 (playerid);
...

plus create trigger on playerID

我想获取最后一个 id(我也可以获取序列的值,但我习惯于处理表/列),但是 pSQL 扫描表似乎相当愚蠢:

解释分析从 mc_handstats 中选择 max(id); (真正的查询永远运行)

"Aggregate  (cost=9080859.04..9080859.05 rows=1 width=4) (actual time=181867.626..181867.626 rows=1 loops=1)"
"  ->  Append  (cost=0.00..8704322.43 rows=150614644 width=4) (actual time=2.460..163638.343 rows=151134891 loops=1)"
"        ->  Seq Scan on mc_handstats  (cost=0.00..0.00 rows=1 width=4) (actual time=0.002..0.002 rows=0 loops=1)"
"        ->  Seq Scan on mc_handst_0000 mc_handstats  (cost=0.00..728523.69 rows=12580969 width=4) (actual time=2.457..10800.539 rows=12656647 loops=1)"
...
ALL TABLES
...
"Total runtime: 181867.819 ms"

解释分析从 mc_handst_1000 中选择 max(id)

"Aggregate  (cost=83999.50..83999.51 rows=1 width=4) (actual time=1917.933..1917.933 rows=1 loops=1)"
"  ->  Seq Scan on mc_handst_1000  (cost=0.00..80507.40 rows=1396840 width=4) (actual time=0.007..1728.268 rows=1396717 loops=1)"
"Total runtime: 1918.494 ms"

分区表的运行时间是“snap”,完全脱离主表的记录。 (postgreSQL 9.2)


\d mc_handstats(仅索引)

Indexes:
    "mc_handst_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
    "mc_handst_playerid_fkey" FOREIGN KEY (playerid) REFERENCES mc_players(id)
Triggers:
    mc_handst_insert_trigger BEFORE INSERT ON mc_handstats FOR EACH ROW EXECUTE PROCEDURE mc_handst_insert_function()
Number of child tables: 20 (Use \d+ to list them.)

\d mc_handst_1000

Indexes:
    "mc_handst_1000_playerid" btree (playerid)
Check constraints:
    "mc_handst_1000_playerid_check" CHECK (playerid >= 1000000 AND playerid < 1100000)

hm,子表中没有 PK 索引。虽然我不明白为什么 max(id) 的结果在子表上非常快(因为没有索引)而从主表上很慢,但似乎我还需要为所有子表添加 PK 索引。也许这样就解决了。


CREATE INDEX mc_handst_0010_ID ON mc_handst_0010 (id);
... plus many more ...

一切都很好。仍然很奇怪为什么它之前在子表上运行得很快,这让我觉得它们被索引了,但我也不太在意。

谢谢!

【问题讨论】:

  • 该表在 playerid 上分区,您搜索 max(id)。除了对所有表进行顺序扫描之外,您为什么希望它做任何其他事情? max(id) 可以在任何表中。 id上也没有索引。
  • ID 是主键,它是自动索引的,不是吗? (当我只对单个表运行查询时,结果很快)
  • Chris:真的是每个分区的PK吗?显示\d mc_handst_1000\d mc_handstats。分区有点……原始……你会发现唯一的约束只适用于一个分区,而不是跨分区;这扩展到主键约束。
  • 不,不是。分区不是通过 PK 完成的,而是通过另一个字段完成的,顺便说一句,这也不是唯一的(但这不是问题)。我将在上面添加 \d。

标签: postgresql database-partitioning


【解决方案1】:

您需要做的第一件事是索引 (id) 上的所有子表,并查看 max(id) 是否足够聪明,可以对每个表进行索引扫描。我想我应该是,但我不完全确定。

如果没有,我会这样做:我会从 currval([sequence_name]) 开始,然后返回直到找到记录。您可以一次检查 10 个块,或者在本质上是稀疏扫描的情况下进行检查。这可以通过这样的 CTE 来完成(再次依赖于索引):

 WITH RECURSIVE ids (
      select max(id) as max_id, currval('mc_handst_id_seq')  - 10 as min_block
        FROM mc_handst
       WHERE id BETWEEN currval('mc_handst_id_seq') - 10 AND currval('mc_handst_id_seq')
      UNION ALL
      SELECT max(id), i.min_block - 10
        FROM mc_handst
        JOIN ids i ON id BETWEEN i.min_block - 10 AND i.min_block
       WHERE i.max_id IS NULL
 )
 SELECT max(max_id) from ids;

如果规划器在分区被索引后不使用索引,则应该进行稀疏扫描。在大多数情况下,它应该只进行一次扫描,但它会根据需要重复查找 id。请注意,它可能会在空表上永远运行。

【讨论】:

    【解决方案2】:

    假设父母的桌子是这样的:

    CREATE TABLE parent AS (
      id not null default nextval('parent_id_seq'::regclass)
      ... other columns ...
    );
    

    无论您是使用规则还是触发器将 INSERT 转移到子表中,都可以在 INSERT 之后立即使用:

    SELECT currval('parent_id_seq'::regclass);
    

    获取会话插入的最后一个 id,与并发 INSERT 无关,每个会话都有自己获得的最后一个序列值的副本。

    https://dba.stackexchange.com/questions/58497/return-id-from-partitioned-table-in-postgres

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-08-29
      • 2015-03-21
      • 2012-12-24
      • 2018-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多