我希望在具有许多非唯一值的字段上的大表(约 5000 万行)上创建索引。
表架构如下所示:
Column | Type | Modifiers | Storage | Stats target | Description
--------+-----------------------+-----------+----------+--------------+-------------
gid | character varying(20) | | extended | |
word | character varying(30) | | extended | |
stat | double precision | | plain | |
Has OIDs: no
Run Code Online (Sandbox Code Playgroud)
我想在 'word' 列上创建一个索引。有一个相当规律的模式,每个单词出现大约 1000 次。我需要做快速SELECT * FROM mytable WHERE word='something';查询。在这些表上创建常规 B 树索引需要大量时间,但确实可以显着提高性能。
由于几个原因,我现在对我的解决方案感到不舒服
(1) B-Tree 索引的选择不是特别积极。是否有替代索引方案在具有高度重复值的字段上表现更好?
(2) 我在一个生产环境中,这些表相当频繁地出现和消失。因为并非所有表都会被大量查询,所以我选择只在触发某些(数据库外)应用程序时在表上构建索引,这样我知道将在表+字段上执行 10k+ 次查询。然而,在创建索引时等待 20 分钟并不理想。情况很微妙;通过创建索引获得的优化与创建索引所需的初始 time-sink 竞争。是否有“更便宜”的索引可以创建?也许整体性能比 B-Tree 稍差,但初始创建成本更低?
首先gid应该是数字类型。integer应该足够好或者bigint密钥空间不应该足够大。占用空间更小,处理速度比字符数据更快,索引更快更小。
更重要的是,为了提高性能,我建议使用数据库规范化。
引用:
有一个相当规律的模式,每个单词出现大约 1000 次。
为唯一词创建一个单独的表:
CREATE TABLE word (
word_id serial
, word text
);
Run Code Online (Sandbox Code Playgroud)
word在您的big_tbl: 中使用独特的实例填充它:
INSERT INTO word (word)
SELECT DISTINCT word
FROM big_tbl
ORDER BY word;
Run Code Online (Sandbox Code Playgroud)
ORDER BY是可选的,手头的查询不需要。但它加快了索引的创建,而且总体上可能更便宜。
相比之下,该表应该很小:大表中的 50M 行只有 ~ 50k 行。填表后
添加索引:
ALTER TABLE word
ADD CONSTRAINT word_word_uni UNIQUE (word) -- essential
, ADD CONSTRAINT word_word_id_pkey PRIMARY KEY (word_id); -- expendable?
Run Code Online (Sandbox Code Playgroud)
如果这些是只读表,则无需 pk 即可。它与手头的操作无关。
用一张小得多的新桌子替换你的大桌子。您可能必须锁定大表以避免并发写入。并发读取不是问题。
CREATE TABLE big_tbl_new AS
SELECT b.gid -- or the suggested smaller, faster numeric replacement
, w.word_id, b.stat
FROM big_tbl b
JOIN word w USING (word)
ORDER BY word; -- sorting by word helps query at hand
Run Code Online (Sandbox Code Playgroud)
ORDER BY集群数据(一次)使手头的查询更快,因为必须读取的块要少得多(除非您的数据大部分已经集群)。排序携带成本,再次权衡成本和收益。
DROP big_tbl; -- make sure your new table has all data!
ALTER big_tbl_new RENAME TO big_tbl;
Run Code Online (Sandbox Code Playgroud)
重新创建索引:
ALTER TABLE big_tbl ADD CONSTRAINT big_tbl_gid_pkey PRIMARY KEY (gid); -- expendable?
CREATE INDEX big_tbl_word_id_idx ON big_tbl (word_id); -- essential
Run Code Online (Sandbox Code Playgroud)
您的查询现在看起来像这样,应该更快:
SELECT b.*
FROM word w
JOIN big_tbl b USING (word_id)
WHERE w.word = 'something';
Run Code Online (Sandbox Code Playgroud)
重组旨在成为重新组织数据的一次性操作。保留新形式并考虑永久保留索引。
所有这些(包括新索引)应该占用您以前在磁盘上的一半左右,同时将创建时间减少一半(至少)。索引创建应该快得多,查询也是如此。如果 RAM 是一个限制因素,那么这些修改将付出双倍的代价。
如果您还必须写入表格,它会变得更加昂贵(但您没有提及任何相关内容)。你需要调整你的逻辑DELETE/ UPDATE/ INSERT:
示例INSERT:获取word_id现有的词或在插入新行word返回新word_id。详细信息:
如何插入包含外键的行?
| 归档时间: |
|
| 查看次数: |
9455 次 |
| 最近记录: |