WHERE 子句:
where子句基于条件表达式来实现数据过滤,若过滤条件恰好是主键字段则能进一步通过索引加助查询,在指标索引特性的表引擎
where子句是一条查询语句能否启用索引的判断依据。
where表达式包含主键 能够通过索引过滤数据区间。
PREWHERE子句:
prewhere 目前只适用于*MergeTree系列的表引擎,可以看做是对where的一种优化,和where语句的作用相同,用来过滤数据。
不同之处在于prewhere首先会读取指定的列数据,来判断数据过滤,等待数据过滤之后再读取select 声明的列字段来补全其余属性。
在某些场合下,prewhere语句比where语句处理的数据量更少性能更高。
相关的参数:
Clickhouse> select * from system.settings where name like '%prewhere%'\G
SELECT *
FROM system.settings
WHERE name LIKE '%prewhere%'
Row 1:
──────
name: optimize_move_to_prewhere
value: 1
changed: 0
description: Allows disabling WHERE to PREWHERE optimization in SELECT queries from MergeTree.
min: ᴺᵁᴸᴸ
max: ᴺᵁᴸᴸ
readonly: 0
type: SettingBool
1 rows in set. Elapsed: 0.002 sec.
Clickhouse> select count(1) from datasets.hits_v1;
SELECT count(1)
FROM datasets.hits_v1
┌─count(1)─┐
│ 8873898 │
└──────────┘
1 rows in set. Elapsed: 0.011 sec.
Clickhouse> select count(1) from datasets.hits_v1 where JavaEnable=1;
SELECT count(1)
FROM datasets.hits_v1
WHERE JavaEnable = 1
┌─count(1)─┐
│ 6535088 │
└──────────┘
1 rows in set. Elapsed: 0.013 sec. Processed 8.87 million rows, 8.87 MB (704.88 million rows/s., 704.88 MB/s.)
Clickhouse> select count(1) from datasets.hits_v1 prewhere JavaEnable=1;
SELECT count(1)
FROM datasets.hits_v1
PREWHERE JavaEnable = 1
┌─count(1)─┐
│ 6535088 │
└──────────┘
1 rows in set. Elapsed: 0.009 sec. Processed 8.87 million rows, 8.87 MB (977.33 million rows/s., 977.33 MB/s.)
可以看到相同的结果,时间变少了,每秒处理的数据吞吐量增加。 根据JavaEnable字段进行了过滤。
测试2:
select WatchID,Title,GoodEvent from datasets.hits_v1 where JavaEnable=1;
6535088 rows in set. Elapsed: 73.843 sec. Processed 8.87 million rows, 863.90 MB
(120.17 thousand rows/s., 11.70 MB/s.)
select WatchID,Title,GoodEvent from datasets.hits_v1 prewhere JavaEnable=1;
6535088 rows in set. Elapsed: 73.282 sec. Processed 8.87 million rows, 863.90 MB
(121.09 thousand rows/s., 11.79 MB/s.)
通常Prewhere性能更优,是都需要将所有的where子句都替换为prewhere呢?其实不必这样,clickhouse提供了自动
化优化的功能,会在条件合适的情况下将where替换为prewhere。默认已经开启了此参数。
不能自动优化的情形:
1.使用常量表达式:
2.使用默认值为alias类型的字段
3.包含了arrayJOIN,globalIn,globalNotIn或者indexHint的查询:
4.select查询的列字段和where的谓词相同:
5.使用了主键字段:
虽然在上述情形不能自动将谓词移动到prewhere,但是仍然可以使用prewhere。以主键字段为例子,当使用了prewhere进行主键查询,
首先会通过稀疏索引过滤数据区间,接着读取prewhere指定的条件列进行过滤,这样就可能戒掉数据区间的尾巴,从而返回
低于index_granularity 粒度的数据范围。
即便如此,相比其他场合移动谓词带来的性能提升,这类效果比较有限,目前在这类场合下仍然保持不移动的处理方式。
参考:
https://clickhouse.tech/docs/en/sql-reference/statements/select/prewhere/
https://clickhouse.tech/docs/en/sql-reference/statements/select/where/