自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

PostgreSQL 中的并發(fā)創(chuàng)建索引

數(shù)據(jù)庫 PostgreSQL
Reindex Concurrently 的主要實現(xiàn)邏輯,首先會根據(jù)傳入的 relationOid,找到所有需要進行 Reindex 的 indexId,并且跳過一些不能進行 Reindex 的索引,例如系統(tǒng) catalog 表不支持 Reindex。

一、初體驗 Create Index Concurrently

在 PostgreSQL 11 之前,創(chuàng)建索引和表數(shù)據(jù)更新是互斥的,也就是說創(chuàng)建索引時會持有一把鎖,這時候任何對表數(shù)據(jù)的增加、更新、刪除操作,都將等待索引創(chuàng)建完成才能繼續(xù)執(zhí)行。

如下面的例子:

  1. 創(chuàng)建示例表
-- 創(chuàng)建測試表,并向其中插入 500w 行隨機字符串數(shù)據(jù)

CREATE TABLE articles (
id SERIAL8 NOT NULL PRIMARY KEY,
a text,
b text,
c text
);

INSERT INTO articles(a, b, c)
SELECT
md5(random()::text),
md5(random()::text),
md5(random()::text)
from (
SELECT * FROM generate_series(1,5000000) AS id
) AS x;
  1. 打開一個 psql 客戶端,執(zhí)行創(chuàng)建索引操作
ubuntu=# create index idx_a on articles (a);
  1. 索引創(chuàng)建時打開另一個 psql 客戶端,并向表中插入數(shù)據(jù),此時另一個事務(wù)已經(jīng)持有表鎖了,所以會一直等待事務(wù)結(jié)束之后才會繼續(xù)執(zhí)行
ubuntu=# insert into articles(a, b, c) values ('1', '2', '3');

可以在事務(wù)執(zhí)行期間,通過 pg_locks 表查看事務(wù)持有的鎖,可以看到創(chuàng)建索引的操作占據(jù)了 ShareLock(5 號鎖),插入操作需要獲取 RowExclusiveLock 鎖,而這兩者是互斥的。

ubuntu=# select * from pg_locks where relation = 'articles'::regclass;
locktype | database | relation | page | tuple | virtualxid | transactionid | classid | objid | objsubid | virtualtransaction | pid | mode | granted | fastpath |
waitstart
----------+----------+----------+------+-------+------------+---------------+---------+-------+----------+--------------------+---------+------------------+---------+----------+-
-----------------------------
relation | 2638325 | 2638341 | | | | | | | | 3/22624 | 1236742 | RowExclusiveLock | f | f |
2023-01-13 14:08:32.54543+08
relation | 2638325 | 2638341 | | | | | | | | 4/209 | 1236951 | ShareLock | t | f |
relation | 2638325 | 2638341 | | | | | | | | 6/20 | 1237182 | ShareLock | t | f |
(3 rows)

索引創(chuàng)建和表更新操作的互斥,帶來一個嚴重的后果,那便是如果表數(shù)據(jù)量較大,創(chuàng)建索引的時間可能很長,如果長時間鎖表的話,會導(dǎo)致表無法更新,可能會對在線業(yè)務(wù)產(chǎn)生很大的影響。

于是 PostgreSQL 在 11 版本中支持了并發(fā)創(chuàng)建索引,即 CREATE INDEX CONCURRENTLY,其主要功能是在創(chuàng)建索引的時候,不阻塞表數(shù)據(jù)的更新。

還是看上面的示例,只需要將第一個事務(wù)的 sql 修改為 create index CONCURRENTLY idx_a on articles (a);,那么其他事務(wù)的表數(shù)據(jù)更新操作將會正常執(zhí)行,不會被阻塞。

然后再看其持有的鎖,可以看到已經(jīng)變成了 ShareUpdateExclusiveLock(4 號鎖):

ubuntu=# select * from pg_locks where relation = 'articles'::regclass;
locktype | database | relation | page | tuple | virtualxid | transactionid | classid | objid | objsubid | virtualtransaction | pid | mode | granted | fas
tpath | waitstart
----------+----------+----------+------+-------+------------+---------------+---------+-------+----------+--------------------+---------+--------------------------+---------+----
------+-----------
relation | 2638325 | 2638341 | | | | | | | | 4/214 | 1236951 | ShareUpdateExclusiveLock | t | f
|
(1 row)

在并發(fā)創(chuàng)建索引的時候,如果遇到了不符預(yù)期的錯誤,或者手動取消,那么這個索引將會留在表中,但是被標識為 INVALID,表示這個索引不可用,也就是說將不會使用這個索引進行索引掃描。

后續(xù)可以手動將其 DROP 掉,然后重新建立索引,也可以執(zhí)行 REINDEX CONCURRENTLY 重建索引。

ubuntu=# \d articles
Table "public.articles"
Column | Type | Collation | Nullable | Default
--------+--------+-----------+----------+--------------------------------------
id | bigint | | not null | nextval('articles_id_seq'::regclass)
a | text | | |
b | text | | |
c | text | | |
Indexes:
"articles_pkey" PRIMARY KEY, btree (id)
"idx_body" btree (a) INVALID

注意:CREATE INDEX CONCURRENTLY 不能在事務(wù)塊中執(zhí)行,也就是說我們不能顯式的 begin 開啟事務(wù)然后執(zhí)行 CREATE INDEX CONCURRENTLY。

二、CREATE INDEX CONCURRENTLY 的三個步驟

主要的代碼位置在 https://github.com/postgres/postgres/blob/master/src/backend/commands/indexcmds.c#L488

DefineIndex 方法中主要是處理索引創(chuàng)建的邏輯,方法前面部分主要是做一系列校驗和參數(shù)初始化等,然后調(diào)用 index_create 方法將索引的元信息存儲到 pg_index、pg_class 等表中。

并且如果判斷到不是 concurrently 創(chuàng)建索引的話,這里會直接返回,也就是說這之后的邏輯都是處理 CONCURRENTLY 并發(fā)索引創(chuàng)建的部分。

if (!concurrent)
{
/* Close the heap and we're done, in the non-concurrent case */
table_close(rel, NoLock);

/* If this is the top-level index, we're done. */
if (!OidIsValid(parentIndexId))
pgstat_progress_end_command();

return address;
}

接著上面的代碼往下看,就是 postgres 的并發(fā)創(chuàng)建索引邏輯,主要分為了三個步驟,這部分代碼的注釋也有一些相應(yīng)的說明。

  1. 開啟一個事務(wù),通過 index_create 方法,將索引的元數(shù)據(jù)信息存儲到 pg_class、pg_index 表中,并且標識索引的 indisready 和 indisvalid 屬性為 false,表示目前索引沒有 ready 并且不可用_;_提交事務(wù),并開啟一個新的事務(wù)進入下一階段。

此階段相當于 DefineIndex 的前一部分,和正常的 create index 的邏輯是相同的。

  1. 1. 進入此階段時,需要等待系統(tǒng)中其他正在寫數(shù)據(jù)的事務(wù)提交,因為必須讓新索引的定義對其他所有的事務(wù)都可見,保證 HOT 更新滿足表的索引定義,調(diào)用 WaitForLockers 函數(shù)進行等待 2. 通過 index_concurrently_build 創(chuàng)建索引,同時持有 4 號鎖,不阻塞表的增/刪/改操作;獲取一個 MVCC 快照,將此快照下可見的元組寫入到索引中;此時如果有其他的事務(wù)有新的寫入,將不會插入到索引中,只是保證 HOT 滿足索引的定義;提交事務(wù),將索引設(shè)置為 isready,后續(xù)如果有新的數(shù)據(jù)插入,將會維護這個索引,但是索引仍然是 invalid,不能用于索引掃描。并再開啟一個新的事務(wù),進入到下一階段。
  2. 1. 又要等待當前所有寫事務(wù)都提交,保證所有的事務(wù)都能看到新索引的定義 2. 在第二階段的執(zhí)行過程中,有可能又有新的元組插入到表中,所以需要再獲取一個新的 MVCC 快照,并將新的元組插入到索引中,調(diào)用函數(shù) validate_index 3. 此時還需要一次等待,將指定 xmin 的之前的事務(wù)提交,調(diào)用函數(shù) WaitForOlderSnapshots 4. 最后將索引置為 valid,后續(xù)其他事務(wù)便可以使用該索引進行查詢了

三、Reindex Concurrently

REINDEX 是一個更加復(fù)雜的命令,PostgreSQL 中也是支持對 REINDEX 進行 CONCURRENTLY 操作的,了解了 CREATE INDEX 之后,我們再來看看 Reindex Concurrently 是如何在 PostgreSQL 上執(zhí)行的。

ExecReindex

PostgreSQL 的 REINDEX 的主要邏輯在方法 ExecReindex 中,對 Reindex 的處理分為了三種情況:

  • REINDEX_OBJECT_INDEX(針對索引)
  • REINDEX_OBJECT_TABLE(針對表)
  • REINDEX_OBJECT_SCHEMA、REINDEX_OBJECT_SYSTEM、REINDEX_OBJECT_DATABASE(針對 schema、系統(tǒng)表、整個庫)

圖片

ReindexRelationConcurrently

這個方法是 Reindex Concurrently 的主要實現(xiàn)邏輯,首先會根據(jù)傳入的 relationOid,找到所有需要進行 Reindex 的 indexId,并且跳過一些不能進行 Reindex 的索引,例如系統(tǒng) catalog 表不支持 Reindex。

主要的代碼位置:https://github.com/greenplum-db/gpdb/blob/main/src/backend/commands/indexcmds.c#L3575

拿到需要進行 Reindex 的索引 Oid 之后,然后進入 Reindex Concurrently 的六個階段:

  • 創(chuàng)建新的索引,創(chuàng)建后表中有一個臨時的新的索引,名稱以 idx_ccnew 開頭
  • build 新創(chuàng)建的索引,即掃描全表數(shù)據(jù),構(gòu)建索引的內(nèi)容
  • validate 新創(chuàng)建的索引,將前一個階段新插入的數(shù)據(jù)加入到索引中,這個和 create index concurrently 類似
  • 交換索引,將新創(chuàng)建的索引和它對應(yīng)的需要 reindex 的索引進行交換,舊的索引標識為 invalid
  • 將舊的索引設(shè)置為 dead 狀態(tài),即 indislive、indisready、indisvalid 均為 false
  • 最后將舊的索引 drop 掉

ps. 在 Postgres 的官方文檔中,也有對 Create Index/Reindex Concurrently 的描述,只是沒有深入到代碼細節(jié)之中,可以參考看下這兩個步驟的執(zhí)行步驟。

??https://www.postgresql.org/docs/current/sql-createindex.htmlhttps://www.postgresql.org/docs/current/sql-reindex.html??

責任編輯:武曉燕 來源: roseduan寫字的地方
相關(guān)推薦

2024-07-08 10:48:51

2011-07-20 09:16:02

MongoDB索引稀疏索引

2024-01-02 09:53:36

PostgreSQL數(shù)據(jù)庫創(chuàng)建和管理表

2010-10-26 16:33:54

創(chuàng)建Oracle索引

2011-10-13 09:44:49

MySQL

2011-05-30 10:36:49

MySQL

2010-05-21 10:01:11

MySQL數(shù)據(jù)庫

2024-04-03 09:12:03

PostgreSQL索引數(shù)據(jù)庫

2010-09-29 13:52:33

PostgreSQL

2023-11-30 16:29:16

PostgreSQL數(shù)據(jù)庫Kubernetes

2012-12-04 10:29:47

PostgreSQL索引

2023-05-30 11:52:11

索引冗余索引

2017-09-22 09:20:06

數(shù)據(jù)庫索引

2024-04-16 09:53:56

PostgreSQL數(shù)據(jù)庫優(yōu)化索引

2009-11-19 15:32:50

Oracle索引

2010-11-16 10:21:25

Oracle創(chuàng)建表

2009-11-16 09:05:47

PostgreSQLInnoDB多版本并發(fā)控制

2010-05-11 10:47:13

MySQL索引類型

2011-03-17 13:33:04

索引可選項

2010-11-02 11:36:10

DB2索引創(chuàng)建
點贊
收藏

51CTO技術(shù)棧公眾號