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

Halo数据库并发创建索引

冷狼 2025-01-19
76

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

评论