- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在设计一个主要是只读的数据库,其中包含 300,000 个文档和大约 50,000 个不同的标签,每个文档平均有 15 个标签。目前,我关心的唯一查询是从给定的一组标签中选择所有没有标签的文档。我只对document_id
感兴趣列(结果中没有其他列)。
我的架构本质上是:
CREATE TABLE documents (
document_id SERIAL PRIMARY KEY,
title TEXT
);
CREATE TABLE tags (
tag_id SERIAL PRIMARY KEY,
name TEXT UNIQUE
);
CREATE TABLE documents_tags (
document_id INTEGER REFERENCES documents,
tag_id INTEGER REFERENCES tags,
PRIMARY KEY (document_id, tag_id)
);
In [17]: %timeit all_docs - (tags_to_docs[12345] | tags_to_docs[7654])
100 loops, best of 3: 13.7 ms per loop
stuff=# SELECT document_id AS id FROM documents WHERE document_id NOT IN (
stuff(# SELECT documents_tags.document_id AS id FROM documents_tags
stuff(# WHERE documents_tags.tag_id IN (12345, 7654)
stuff(# );
document_id
---------------
...
Time: 201.476 ms
NOT IN
与 EXCEPT
使它更慢。 document_id
上有 btree 索引和 tag_id
在所有三个表中,另一个位于 (document_id, tag_id)
. EXPLAIN ANALYZE
的结果:
EXPLAIN ANALYZE
SELECT document_id AS id FROM documents
WHERE document_id NOT IN (
SELECT documents_tags.documents_id AS id FROM documents_tags
WHERE documents_tags.tag_id IN (12345, 7654)
);
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
Seq Scan on documents (cost=20280.27..38267.57 rows=83212 width=4) (actual time=176.760..300.214 rows=20036 loops=1)
Filter: (NOT (hashed SubPlan 1))
Rows Removed by Filter: 146388
SubPlan 1
-> Bitmap Heap Scan on documents_tags (cost=5344.61..19661.00 rows=247711 width=4) (actual time=32.964..89.514 rows=235093 loops=1)
Recheck Cond: (tag_id = ANY ('{12345,7654}'::integer[]))
Heap Blocks: exact=3300
-> Bitmap Index Scan on documents_tags__tag_id_index (cost=0.00..5282.68 rows=247711 width=0) (actual time=32.320..32.320 rows=243230 loops=1)
Index Cond: (tag_id = ANY ('{12345,7654}'::integer[]))
Planning time: 0.117 ms
Execution time: 303.289 ms
(11 rows)
Time: 303.790 ms
shared_buffers = 5GB
temp_buffers = 128MB
work_mem = 512MB
effective_cache_size = 16GB
最佳答案
优化设置以提高读取性能
您的内存设置对于 64GB 服务器来说似乎合理 - 除了 work_mem = 512MB
.那是高。您的查询不是特别复杂,您的表也不是那么大。
简单连接表中的 450 万行 (300k x 15) documents_tags
应该占用 ~ 156 MB 和 PK 另一个 96 MB。对于您的查询,您通常不需要读取整个表,只需读取索引的一小部分。对于“ 大部分是只读的 ”,就像您一样,您应该看到 仅索引扫描 仅在 PK 的索引上。您几乎不需要那么多 work_mem
- 这可能无关紧要 - 除非您有许多并发查询。 Quoting the manual :
... several running sessions could be doing such operations concurrently. Therefore, the total memory used could be many times the value of
work_mem
; it is necessary to keep this fact in mind when choosing the value.
work_mem
太高实际上可能会损害性能:
work_mem
到 128 MB 或更少以避免可能的内存不足 - 除非您有其他需要更多的常见查询。对于特殊查询,您始终可以在本地将其设置得更高。
PRIMARY KEY (document_id,
tag_id)
tag_id
如
领先 列和
document_id
作为第二。只有
(tag_id)
上的索引您无法获得仅索引扫描。如果此查询是您唯一的用例,请按如下所示更改您的 PK。
(tag_id, document_id)
上创建一个额外的普通索引如果您同时需要 - 并删除
documents_tags
上的其他两个索引就在
(tag_id)
和
(document_id)
.它们对两个多列索引没有任何提供。剩余
2 索引(与之前的
3 索引相反)在各方面都更小且更优越。理由:
CLUSTER
使用新 PK 的表,全部在一个事务中,可能还有一些额外的
maintenance_work_mem
本地:
BEGIN;
SET LOCAL maintenance_work_mem = '256MB';
ALTER TABLE documents_tags
DROP CONSTRAINT documents_tags_pkey
, ADD PRIMARY KEY (tag_id, document_id); -- tag_id first.
CLUSTER documents_tags USING documents_tags_pkey;
COMMIT;
ANALYZE documents_tags;
NOT IN
是 - 引用我自己的话:
Only good for small sets without NULL values
NOT NULL
并且您的排除项目 list 很短。您的原始查询是一个热门竞争者。
NOT EXISTS
和
LEFT JOIN / IS NULL
总是热门的竞争者。两者都已在其他答案中提出。
LEFT JOIN
必须是实际的
LEFT [OUTER] JOIN
, 尽管。
EXCEPT ALL
将是最短的,但通常没有那么快。
SELECT document_id
FROM documents d
WHERE document_id NOT IN (
SELECT document_id -- no need for column alias, only value is relevant
FROM documents_tags
WHERE tag_id IN (12345, 7654)
);
SELECT document_id
FROM documents d
WHERE NOT EXISTS (
SELECT 1
FROM documents_tags
WHERE document_id = d.document_id
AND tag_id IN (12345, 7654)
);
SELECT d.document_id
FROM documents d
LEFT JOIN documents_tags dt ON dt.document_id = d.document_id
AND dt.tag_id IN (12345, 7654)
WHERE dt.document_id IS NULL;
SELECT document_id
FROM documents
EXCEPT ALL -- ALL, to keep duplicate rows and make it faster
SELECT document_id
FROM documents_tags
WHERE tag_id IN (12345, 7654);
SET random_page_cost = 1.1;
SET work_mem = '128MB';
CREATE SCHEMA temp;
SET search_path = temp, public;
CREATE TABLE documents (
document_id serial PRIMARY KEY,
title text
);
-- CREATE TABLE tags ( ... -- actually irrelevant for this query
CREATE TABLE documents_tags (
document_id integer REFERENCES documents,
tag_id integer -- REFERENCES tags -- irrelevant for test
-- no PK yet, to test seq scan
-- it's also faster to create the PK after filling the big table
);
INSERT INTO documents (title)
SELECT 'some dummy title ' || g
FROM generate_series(1, 300000) g;
INSERT INTO documents_tags(document_id, tag_id)
SELECT i.*
FROM documents d
CROSS JOIN LATERAL (
SELECT DISTINCT d.document_id, ceil(random() * 50000)::int
FROM generate_series (1,15)) i;
ALTER TABLE documents_tags ADD PRIMARY KEY (document_id, tag_id); -- your current index
ANALYZE documents_tags;
ANALYZE documents;
documents_tags
中的行物理集群由
document_id
由于我填表的方式 - 这也可能是你目前的情况。
documents_tags_pkey
就像你拥有它一样。行的索引和物理顺序对我们的查询不利。
(tag_id, document_id)
上重新创建 PK喜欢建议。
CLUSTER
在新PK上。
EXPLAIN ANALYZE
的执行时间毫秒:
tag_id
- 对于涉及少数 tag_id
的查询和许多 document_id
.document_id
比tag_id
.这也可能是相反的。 Btree 索引对于任何列顺序都基本相同。事实上,查询中最具选择性的谓词过滤了 tag_id
.这在领先的索引列上更快。 tag_id
排除是您原来的 NOT IN
. NOT EXISTS
和 LEFT JOIN / IS NULL
导致相同的查询计划。对于几十个 ID,我希望它们能够更好地扩展... 关于sql - 优化行排除查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38807937/
SQL、PL-SQL 和 T-SQL 之间有什么区别? 谁能解释一下这三者之间的区别,并提供每一个的相关使用场景? 最佳答案 SQL 是一种对集合进行操作的查询语言。 它或多或少是标准化的,几乎所有关
这个问题已经有答案了: What is the difference between SQL, PL-SQL and T-SQL? (6 个回答) 已关闭 9 年前。 我对 SQL 的了解足以完成我的
我在数据库中有一个 USER 表。该表有一个 RegistrationDate 列,该列有一个默认约束为 GETDATE()。 使用 LINQ 时,我没有为 RegistrationDate 列提供任
我有一个可能属于以下类型的字符串 string expected result 15-th-rp 15 15/12-rp 12 15-12-th
很难说出这里问的是什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或言辞激烈,无法以目前的形式合理回答。如需帮助澄清此问题以便可以重新打开,visit the help center . 9年前关闭
我有一个存储过程(称为 sprocGetArticles),它从文章表中返回文章列表。这个存储过程没有任何参数。 用户可以对每篇文章发表评论,我将这些评论存储在由文章 ID 链接的评论表中。 有什么方
我目前正在做一个 *cough*Oracle*cough* 数据库主题。讲师介绍embedded SQL作为让其他语言(例如 C、C++)与(Oracle)数据库交互的方式。 我自己做了一些数据库工作
SQL Server 中 SQL 语句的最大长度是多少?这个长度是否取决于 SQL Server 的版本? 例如,在 DECLARE @SQLStatement NVARCHAR(MAX) = N'S
这个问题已经有答案了: Simple way to transpose columns and rows in SQL? (9 个回答) 已关闭 8 年前。 CallType
预先感谢您对此提供的任何帮助。 假设我有一个查询,可以比较跨年的数据,从某个任意年份开始,永无止境(进入 future ),每年同一时期直到最后一个完整的月份(其特点是一月数据永远不会显示至 2 月
我在数据库中有一个 USER 表。该表有一个 RegistrationDate 列,该列的默认约束为 GETDATE()。 使用 LINQ 时,我没有为 RegistrationDate 列提供任何数
下面是我试图用来检查存储过程是否不存在然后创建过程的 sql。它会抛出一个错误:Incorrect syntax near the keyword 'PROCEDURE' IF NOT EXISTS
我有一个同事声称动态 SQL 在许多情况下比静态 SQL 执行得更快,所以我经常看到 DSQL 到处都是。除了明显的缺点,比如在运行之前无法检测到错误并且更难阅读,这是否准确?当我问他为什么一直使用
来自 lobodava 的动态 SQL 查询是: declare @sql nvarchar(4000) = N';with cteColumnts (ORDINAL_POSITION, CO
使用 SQL Server 中的存储过程执行动态 SQL 命令的现实优点和缺点是什么 EXEC (@SQL) 对比 EXEC SP_EXECUTESQL @SQL ? 最佳答案 sp_executes
我有这个有效的 SQL 查询: select sum(dbos.Points) as Points, dboseasons.Year from dbo.StatLines dbos i
我正在调试一些构建成功运行的 SQL 命令的代码。 然而,在查询结束时,查询结果似乎被写入了一个文本文件。 完整的查询如下 echo SELECT DATE,DATETABLE,DATE,APPDAT
我有一些创建表的 .sql 文件(MS SQL 数据库): 表_1.sql: IF OBJECT_ID (N'my_schema.table1', N'U') IS NOT NULL DROP TAB
我写了下面的 SQL 存储过程,它一直给我错误@pid = SELECT MAX(... 整个过程是: Alter PROCEDURE insert_partyco @pname varchar(20
我在 SQL Server 2005 中有包含两列 Fruit 和 Color 的表,如下所示 Fruit Colour Apple Red Orange
我是一名优秀的程序员,十分优秀!