检查主键使用情况
创建表
CREATE TABLE logs
(
`code` LowCardinality(String),
`timestamp` DateTime64(3)
)
ENGINE = MergeTree
ORDER BY (code, toUnixTimestamp(timestamp))
toUnixTimestamp(timestamp)。
填充数据
INSERT INTO logs SELECT
['200', '404', '502', '403'][toInt32(randBinomial(4, 0.1)) + 1] AS code,
now() + toIntervalMinute(number) AS timestamp
FROM numbers(100000000)
0 rows in set. Elapsed: 15.845 sec. Processed 100.00 million rows, 800.00 MB (6.31 million rows/s., 50.49 MB/s.)
SELECT count()
FROM logs
┌───count()─┐
│ 100000000 │ -- 1 亿
└───────────┘
1 row in set. Elapsed: 0.002 sec.
基本过滤
49.15 thousand。请注意,这只是总计 1 亿行中的一部分。
SELECT count() AS c
FROM logs
WHERE code = '200'
┌────────c─┐
│ 65607542 │ -- 6561 万
└──────────┘
1 行记录。耗时:0.021 秒。已处理 4.915 万行,49.17 KB(每秒 234 万行,2.34 MB/s。)
峰值内存占用:92.70 KiB。
EXPLAIN indexes=1 子句来确认已使用该索引:
EXPLAIN indexes = 1
SELECT count() AS c
FROM logs
WHERE code = '200'
┌─explain────────────────────────────────────────────────────────────┐
│ Expression ((Project names + Projection)) │
│ AggregatingProjection │
│ Expression (Before GROUP BY) │
│ Filter ((WHERE + Change column names to column identifiers)) │
│ ReadFromMergeTree (default.logs) │
│ Indexes: │
│ PrimaryKey │
│ Keys: │
│ code │
│ Condition: (code in ['200', '200']) │
│ Parts: 3/3 │
│ Granules: 8012/12209 │
│ ReadFromPreparedSource (_minmax_count_projection) │
└────────────────────────────────────────────────────────────────────┘
8012 仅占总数 12209 的一部分。下方高亮的部分表明这里使用了主键。
PrimaryKey
Keys:
code
对排序键中靠后的键进行过滤,其效率不如对元组中靠前的键进行过滤。原因请参见这里
按多个键过滤
code 和 timestamp 进行过滤:
SELECT count()
FROM logs
WHERE (code = '200') AND (timestamp >= '2025-01-01 00:00:00') AND (timestamp <= '2026-01-01 00:00:00')
┌─count()─┐
│ 689742 │
└─────────┘
1 row in set. Elapsed: 0.008 sec. Processed 712.70 thousand rows, 6.41 MB (88.92 million rows/s., 799.27 MB/s.)
EXPLAIN indexes = 1
SELECT count()
FROM logs
WHERE (code = '200') AND (timestamp >= '2025-01-01 00:00:00') AND (timestamp <= '2026-01-01 00:00:00')
┌─explain───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Expression ((Project names + Projection)) │
│ Aggregating │
│ Expression (Before GROUP BY) │
│ Expression │
│ ReadFromMergeTree (default.logs) │
│ Indexes: │
│ PrimaryKey │
│ Keys: │
│ code │
│ toUnixTimestamp(timestamp) │
│ Condition: and((toUnixTimestamp(timestamp) in (-Inf, 1767225600]), and((toUnixTimestamp(timestamp) in [1735689600, +Inf)), (code in ['200', '200']))) │
│ Parts: 3/3 │
│ Granules: 87/12209 │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
13 rows in set. Elapsed: 0.002 sec.
87 个粒度。
在排序中使用键
SELECT *
FROM logs
WHERE (code = '200') AND (timestamp >= '2025-01-01 00:00:00') AND (timestamp <= '2026-01-01 00:00:00')
ORDER BY timestamp ASC
LIMIT 10
┌─code─┬───────────────timestamp─┐
│ 200 │ 2025-01-01 00:00:01.000 │
│ 200 │ 2025-01-01 00:00:45.000 │
│ 200 │ 2025-01-01 00:01:01.000 │
│ 200 │ 2025-01-01 00:01:45.000 │
│ 200 │ 2025-01-01 00:02:01.000 │
│ 200 │ 2025-01-01 00:03:01.000 │
│ 200 │ 2025-01-01 00:03:45.000 │
│ 200 │ 2025-01-01 00:04:01.000 │
│ 200 │ 2025-01-01 00:05:45.000 │
│ 200 │ 2025-01-01 00:06:01.000 │
└──────┴─────────────────────────
10 行,耗时 0.009 秒。已处理 712.70 千行,6.41 MB(80.13 百万行/秒,720.27 MB/秒)
峰值内存占用:125.50 KiB。
EXPLAIN pipeline 确认这里并未应用该优化:
EXPLAIN PIPELINE
SELECT *
FROM logs
WHERE (code = '200') AND (timestamp >= '2025-01-01 00:00:00') AND (timestamp <= '2026-01-01 00:00:00')
ORDER BY timestamp ASC
LIMIT 10
┌─explain───────────────────────────────────────────────────────────────────────┐
│ (Expression) │
│ ExpressionTransform │
│ (Limit) │
│ Limit │
│ (Sorting) │
│ MergingSortedTransform 12 → 1 │
│ MergeSortingTransform × 12 │
│ LimitsCheckingTransform × 12 │
│ PartialSortingTransform × 12 │
│ (Expression) │
│ ExpressionTransform × 12 │
│ (Expression) │
│ ExpressionTransform × 12 │
│ (ReadFromMergeTree) │
│ MergeTreeSelect(pool: ReadPool, algorithm: Thread) × 12 0 → 1 │
└───────────────────────────────────────────────────────────────────────────────┘
15 rows in set. Elapsed: 0.004 sec.
MergeTreeSelect(pool: ReadPool, algorithm: Thread) 这一行并不表示使用了该优化,而是表示一次常规读取。这是因为我们的表排序键使用的是 toUnixTimestamp(Timestamp),而不是 timestamp。修正这种不匹配即可解决该问题:
EXPLAIN PIPELINE
SELECT *
FROM logs
WHERE (code = '200') AND (timestamp >= '2025-01-01 00:00:00') AND (timestamp <= '2026-01-01 00:00:00')
ORDER BY toUnixTimestamp(timestamp) ASC
LIMIT 10
┌─explain──────────────────────────────────────────────────────────────────────────┐
│ (Expression) │
│ ExpressionTransform │
│ (Limit) │
│ Limit │
│ (Sorting) │
│ MergingSortedTransform 3 → 1 │
│ BufferChunks × 3 │
│ (Expression) │
│ ExpressionTransform × 3 │
│ (Expression) │
│ ExpressionTransform × 3 │
│ (ReadFromMergeTree) │
│ MergeTreeSelect(pool: ReadPoolInOrder, algorithm: InOrder) × 3 0 → 1 │
└──────────────────────────────────────────────────────────────────────────────────┘
13 rows in set. Elapsed: 0.003 sec.