通常情况下,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
复制