暂无图片
暂无图片
2
暂无图片
暂无图片
暂无图片

Halo数据库并发创建索引

贾桂权 2024-11-19
149

Halo数据库并发创建索引

通常情况下,曦和Halo数据库在创建索引的时会锁定表以防止数据写入,然后对表做全表扫描,最终完成创建索引的操作。在此过程中,其他用户仍然可以读取表数据,但是插入、更新、删除等操作将一直被阻塞,直到索引创建完毕。如果这张表是数据更新较频繁且数据量比较大的表,那么创建索引可能需要几十分钟,甚至数个小时,这段时间内都不能做任何插入、删除、更新操作,这在大多数的生产数据库中都是不可接受的。鉴于此,Halo数据库支持在不长时间阻塞更新的情况下建立创建索引,这是通过在CREATE INDEX中加CONCURRENTLY选项来实现的。当该选项被启用时,Halo数据库会执行表的两次扫描,因此该方法需要更长的时间来建索引。尽管如此,该选项也是很有用的。

下面来做一个测试。

先建一张测试表:

test=# CREATE TABLE test01(id int primary key, name varchar(50));

插入测试数据:

test=# INSERT INTO test01 select generate_series(1,5000000), generate_series(1,5000000);

这时打开两个psql窗口,在一个窗口中建索引:

[halo@HaloTest ~]$ psql test

psql (14.6 (230203))

输入 "help" 来获取帮助信息.

 

test=# \timing

启用计时功能.

test=# CREATE INDEX idx_test01_name on test01(name);

CREATE INDEX

时间:8304.363 ms (00:08.304)

 

在另一个窗口中立即删除一条数据,我们可以看到,它一直在等另一个窗口中创建索引的操作完成:

[halo@HaloTest ~]$ hsql -d test

psql (14.6 (230203))

输入 "help" 来获取帮助信息.

 

test=# \timing

启用计时功能.

test=# DELETE FROM test01 where id=1;

DELETE 1

时间:6425.432 ms (00:06.425)

 

在创建索引时启用CONCURRENTLY选项,命令如下:

test=# DROP INDEX idx_test01_name;

DROP INDEX

时间:21.801 ms

test=# CREATE INDEX CONCURRENTLY idx_test01_name on test01(name);

CREATE INDEX

时间:10344.364 ms (00:10.344)

 

立即另一个窗口的删除操作,我们可以看到删除会立即执行,命令如下:

test=# DELETE FROM test01 where id=2;

DELETE 1

时间:0.746 ms

test=# DELETE FROM test01 where id=3;

DELETE 1

时间:0.185 ms

 

如果想要重建频繁更新的表上的索引,要怎么做?

在Halo数据库中,重建索引不支持CONCURRENTLY选项,但Halo数据库中是允许在同个字段上创建两个甚至更多索引的,因此可以考虑这样做:使用CONCURRENTLY选项建一个新的索引,然后把旧索引删除掉,这样就相当于重建了这个索引,命令如下。

test=# CREATE INDEX CONCURRENTLY idx_test01_name_1 on test01(name);

CREATE INDEX

时间:9203.858 ms (00:09.204)

test=# CREATE INDEX CONCURRENTLY idx_test01_name_2 on test01(name);

CREATE INDEX

时间:10044.115 ms (00:10.044)

test=# DROP INDEX idx_test01_name_1;

DROP INDEX

时间:16.057 ms

test=# DROP INDEX idx_test01_name;

DROP INDEX

时间:40.786 ms

test=# \d test01

                  数据表 "public.test01"

 栏位 |         类型          | 校对规则 |  可空的  | 预设

------+-----------------------+----------+----------+------

 id   | integer               |          | not null |

 name | character varying(50) |          |          |

索引:

    "test01_pkey" PRIMARY KEY, btree (id)

    "idx_test01_name_2" btree (name)

 

并发创建索引的时候需要注意,如果在索引创建过程中被强行取消可能会留下一个无效的索引,这个索引仍然会导致更新速度变慢。如果所创建的是一个唯一索引,这个无效的索引还会导致插入重复值失败,测试示例如下。

先在创建过程中取消操作,命令如下:

test=# CREATE INDEX CONCURRENTLY idx_test01_name on test01(name);

^C取消发送的请求

错误:  由于用户请求而正在取消查询

时间:3003.538 ms (00:03.004)

 

然后使用“\d”命令查看表,可以看到遗留了一个INVALID索引:

test=# \d test01

                  数据表 "public.test01"

 栏位 |         类型          | 校对规则 |  可空的  | 预设

------+-----------------------+----------+----------+------

 id   | integer               |          | not null |

 name | character varying(50) |          |          |

索引:

    "test01_pkey" PRIMARY KEY, btree (id)

    "idx_test01_name" btree (name) INVALID

    "idx_test01_name_2" btree (name)

 

查看未完成的索引仍然是占用磁盘空间的,建议删除:

test=# SELECT pg_size_pretty(pg_relation_size('idx_test01_name'));

 pg_size_pretty

----------------

 56 MB

(1 行记录)

 

时间:1.051 ms

test=# DROP INDEX idx_test01_name;

DROP INDEX

时间:13.404 ms

 

如果在创建唯一索引时发现有重复数据,也会导致产生一个无效的索引,手动删除即可

test=# CREATE UNIQUE INDEX CONCURRENTLY idx_test01_name on test01(name);

错误:  无法创建唯一索引"idx_test01_name"

描述:  键值(name)=(5)重复了

时间:3955.552 ms (00:03.956)

test=# \d test01

                  数据表 "public.test01"

 栏位 |         类型          | 校对规则 |  可空的  | 预设

------+-----------------------+----------+----------+------

 id   | integer               |          | not null |

 name | character varying(50) |          |          |

索引:

    "test01_pkey" PRIMARY KEY, btree (id)

    "idx_test01_name" UNIQUE, btree (name) INVALID

    "idx_test01_name_2" btree (name)

访问方法 heap

 

test=# DROP INDEX idx_test01_name;

DROP INDEX

时间:2.306 ms

 

 

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论