来源:安瑞哥是码农
所谓的「点查询」,指的是在查询数据库表时,通过等值的条件筛选(where xxx=yyy),一般通过走索引的方式,以非常快的速度,获取到目标结果的查询方式。
一般对于数据库来说,如果你查询的条件走了索引,且符合条件的目标数据量很小的话,那么这个查询效率就会非常高。
至于说在已经利用了索引的基础上,还能玩出什么花来,Doris 宣称,它可以通过额外的一些设置,进一步实现查询加速。
那么今天这篇文章,就来一起看看这朵花,在针对 Doris 提到的点查询场景时,能开出什么样的颜色?
0. 官网描述
自 Doris 2.0 之后(当前进行测试的版本为2.1.2),官方提供了为点查询加速的「三板斧」,这三板斧分别是:
1. 开启行存:正常情况下 Doris 的数据都是以列的方式进行存储,这个所谓的行存,本质是另外开辟出一列,来存储整行的数据,理论上,是一个典型的「用空间换效率」的玩法;
2. 使用 PreparedStatement:这个应该是传统的 SQL 数据库都具备的特性,这里把它归结到可以用来加速点查询,个人认为有点牵强;
3. 开启行缓存:任何一个成熟的数据库,能够利用系统缓存(操作系统缓存,或者数据库进程空间的缓存)是一件特别正常的事情,不应该把这个作为自己查询加速的手段。而且,这个只能通过修改 BE 端的配置文件来实现,比较麻烦。
只不过这三板斧呢,除了第一把我觉得可能比较锋利,比较有兴趣之外,剩下的两把,等后面有时间了再试吧。
所以这次测试,咱就只针对这个「开启行存」功能来展开。
1. 测试设计
还是用之前那份上网日志数据集,参考官网提供的测试样例,分别写到两张 UNIQUE 表里,其中一张开启点查询加速,另一张啥也不干(默认配置),然后对比同一个查询,看他们的效率分别如何。
找到一张数据量8亿+的测试母表,一共9个字段。
然后将这张表的数据,分别灌入到下面两张开启了「行存」的 UNIQUE 表,和没有开启行存功能的表。
开启了「行存」的 UNIQUE 表结构:
CREATE TABLE `point_query_test01` (
`client_ip` VARCHAR(200) ,
`domain` VARCHAR(200) ,
`time` VARCHAR(30) ,
`target_ip` TEXT ,
`rcode` INT NULL DEFAULT "99",
`query_type` INT NULL DEFAULT "99",
`authority_record` TEXT ,
`add_msg` TEXT ,
`dns_ip` TEXT
) ENGINE=OLAP
UNIQUE KEY(`client_ip`, `domain`, `time`)
COMMENT 'OLAP'
DISTRIBUTED BY HASH(`client_ip`, `domain`, `time`) BUCKETS 3
PROPERTIES (
"replication_allocation" = "tag.location.default: 1",
"enable_unique_key_merge_on_write" = "true",
"light_schema_change" = "true",
"store_row_column" = "true"
);
没开启「行存」的 UNIQUE 表结构:
CREATE TABLE `point_query_test02` (
`client_ip` VARCHAR(200) ,
`domain` VARCHAR(200) ,
`time` VARCHAR(30) ,
`target_ip` TEXT ,
`rcode` INT NULL DEFAULT "99",
`query_type` INT NULL DEFAULT "99",
`authority_record` TEXT ,
`add_msg` TEXT ,
`dns_ip` TEXT
) ENGINE=OLAP
UNIQUE KEY(`client_ip`, `domain`, `time`)
COMMENT 'OLAP'
DISTRIBUTED BY HASH(`client_ip`, `domain`, `time`) BUCKETS 3
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);
2. 灌数据
先看开启了行存功能的表,数据写入的情况:
耗时 17 分 36 秒。
再看没有开启行存功能表的数据写入情况:
耗时 9 分 06 秒。
从写入耗时来看,开启了行存功能的表,其写入时间,几乎是没有开启行存表的两倍。
在数据量一样的情况下,再对比一下这两张表所占用的存储大小:
可以看到,开启了行存功能的存储占用,是没有开启行存的3倍还多。
3. 查询效率对比
既然数据写入效率更慢,而且占用的存储也更多,那我们接下来倒要看看,对于开启了行存功能的表来说,这亏欠的两个部分,到底能不能从查询效率上,给找补回来?
官网对这个能利用到行存功能的条件(短查询路径),是必须要对 key 进行等值筛选,但它没有详细说明,当这个 key 是组合字段时(当前测试的表就是3个字段的组合 key),我可不可以基于「最左原则」,也能利用到这个功能的便利性。
如果筛选条件可以基于「最左原则」,那说明这个功能设计的还算正常,而如果不行,只能说明这个设计太失败了(一般情况下应该不会)。
但不管它如何设计,下面我将根据人类使用索引的正常逻辑,遵循 where 条件中,字段筛选的「最左原则」进行测试。
点查对比1
查询 SQL(where 条件含 key 中1个字段情况下):
select * from point_query_test where client_ip='19.168.200.160
开启了行存功能的效率:
没有开启行存的效率:
你可能会说,单次测试说明不了什么问题,但事实是,我换了多个不同的 client_ip 测试了多次,结果一样。
测试小结:开启了行存的查询效率,都没能干过不开启行存的,挑战失败(数量级的差别)。
点查对比2
查询 SQL(where 条件含 key 中2个字段情况下):
select * from point_query_test where client_ip='192.168.200.115' and domain='qq.coM.'
开启了行存功能的效率:
没有开启行存的效率:
同样的,这一次我还是把查询字段中的值换了多个,依然是前者的效率,不如后者。
测试小结:开启了行存的查询效率,干不过不开启行存的(数量级的差别)。
点查询对比3
查询 SQL(where 条件含所有 key 字段情况下):
select * from point_query_test where client_ip='192.168.100.87' and domain='www.BaIdU.CoM.' and time='20230706081205';
开启了行存功能的效率:
没有开启行存的效率:
也是一样,换了多个不同的查询值,两者的执行效率依然是同样的水平。
测试小结:还是一样,开启了行存的查询效率,依然干不过不开启行存的(数量级的差别)。
点查询对比4
查询 SQL(where 条件为非 key 字段):
select * from point_query_test where target_ip='8.7.198.46';
开启了行存功能的效率:
没有开启行存的效率:
小结:在查询条件不包含 key 的字段情况下,查询效率打平。
以上的查询效率对比,取的都是各自第一次查询出结果的时间,因为只有这样才最有意义。
当然,我也测试过同一个查询条件,后续再次查询的效率,估计是受系统缓存的影响,两者的后续查询效率几乎一致。
最后
从这次针对点查询优化而开启的行存功能实测表现来看(仅针对当前数据场景),对于同样的一份数据集,在开启了行存情况下,数据写入效率变低(大概减半),且存储空间更大(是原来的3倍)。
但是,即便如此,却并没有换来该有的「点查询」效率变高,而且恰恰相反,变更低了(是我使用的姿势不对吗?)。
老实说,对比到这里,我心里都开始有点慌了,怀疑自己是不是眼花,把测试结论给整反了,在反复确认之后,才算松了一个口气。
也不知道是不是我的测试场景过于与众不同,从以往测试过那么多 Doris 推出的,有关查询优化的功能来看,能做到不打脸的,好像还真不多。
我想说:实测,才是检验一个功能到底行不行最有说服力的标准,没有之一。
至于说在高并发情况下,这种优化是不是真的有效,还期待你们的检验。