6.3 高并发点查¶
Tip
高并发点查功能自 Doris 2.0
版本起具有重大性能提升
1 背景¶
Doris
基于列存格式引擎构建,在高并发服务场景中,用户总是希望从系统中获取整行数据。但是,当表宽时,列存格式将大大放大随机读取 IO
。 Doris
查询引擎和计划对于某些简单的查询(如点查询)来说太重了。需要一个在 FE
的查询规划中规划短路径来处理这样的查询。 FE
是 SQL
查询的访问层服务,使用 Java
编写,分析和解析 SQL
也会导致高并发查询的高 CPU
开销。为了解决上述问题,我们在 Doris
中引入了行存、短查询路径、 PreparedStatement
来解决上诉问题,下面是开启这些优化的指南。
2 行存¶
用户可以在 Olap
表中开启行存模式,但是需要额外的空间来存储行存。目前的行存实现是将行存编码后存在单独的一列中,这样做是用于简化行存的实现。行存模式仅支持在建表的时候开启,需要在建表语句的 property
中指定如下属性:
SQL | |
---|---|
1 |
|
3 在 Unique 模型下的点查优化¶
上述的行存用于在 Unique
模型下开启 Merge-On-Write
策略是减少点查时的 IO
开销。当 enable_unique_key_merge_on_write
与 store_row_column
在创建 Unique
表开启时,对于主键的点查会走短路径来对 SQL
执行进行优化,仅需要执行一次 RPC
即可执行完成查询。下面是点查结合行存在 Unique
模型下开启 Merge-On-Write
策略的一个例子:
SQL | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
注意:
-
enable_unique_key_merge_on_write
应该被开启,存储引擎需要根据主键来快速点查 -
当条件只包含主键时,如
select * from tbl_point_query where key = 123
,类似的查询会走短路径来优化查询 -
light_schema_change
应该被开启,因为主键点查的优化依赖了轻量级Schema Change
中的column unique id
来定位列 -
只支持单表
key
列等值查询不支持join
、嵌套子查询,where
条件里需要有且仅有key
列的等值,可以认为是一种key value
查询 -
开启行存会导致空间膨胀,占用更多的磁盘空间,如果只需要查询部分列,在
Doris 2.1
后建议使用"row_store_columns"="key,v1,v2"
类似的方式指定部份列作为行存,查询的时候只查询这部份列,例如SQL 1
SELECT `key`, v1, v2 FROM tbl_point_query WHERE key = 1
4 使用 PreparedStatement¶
为了减少 SQL
解析和表达式计算的开销,我们在 FE
端提供了与 MySQL
协议完全兼容的 PreparedStatement
特性(目前只支持主键点查)。当 PreparedStatement
在 FE
开启, SQL
和其表达式将被提前计算并缓存到 Session
级别的内存缓存中,后续的查询直接使用缓存对象即可。当 CPU
成为主键点查的瓶颈,在开启 PreparedStatement
后,将会有 4
倍以上的性能提升。下面是在 JDBC
中使用 PreparedStatement
的例子
-
设置
JDBC url
并在Server
端开启prepared statement
Java 1
url = jdbc:mysql://127.0.0.1:9030/ycsb?useServerPrepStmts=true
-
使用
PreparedStatement
Java 1 2 3 4 5 6 7 8 9
// use `?` for placement holders, readStatement should be reused PreparedStatement readStatement = conn.prepareStatement("select * from tbl_point_query where key = ?"); ... readStatement.setInt(1,1234); ResultSet resultSet = readStatement.executeQuery(); ... readStatement.setInt(1,1235); resultSet = readStatement.executeQuery(); ...
5 开启行缓存¶
Doris
中有针对 Page
级别的 Cache
,每个 Page
中存的是某一列的数据,所以 Page cache
是针对列的缓存,对于前面提到的行存,一行里包括了多列数据,缓存可能被大查询给刷掉,为了增加行缓存命中率,单独引入了行存缓存,行缓存复用了 Doris
中的 LRU Cache
机制来保障内存的使用,通过指定下面的的 BE
配置来开启
-
disable_storage_row_cache
是否开启行缓存,默认不开启 -
row_cache_mem_limit
指定Row cache
占用内存的百分比,默认20%
内存
6 性能优化¶
-
通常,通过增加
Observer
数量来提升处理query
能力是有效的 -
query
负载均衡:点查中如果发现接受点查请求的fe cpu
使用过高,或请求响应变慢,可使用jdbc load balance
进行负载均衡,将请求分散到多个节点,分担压力(同时也可以使用其他方式进行query
负载均衡配置,如Nginx
,proxySQL
) -
通过将点查请求定向发送至
Observer
角色来分担高并发点查的请求压力,减少向fe master
发送点查请求,通常可以解决Fe Master
节点查询耗时上下浮动问题,以获得更好性能与稳定性
7 FAQ¶
7.1 Q1. 如何确定配置无误使用了并发点查的短路径优化¶
A
: explain sql
,当执行计划中出现 SHORT-CIRCUIT
,证明使用了短路径优化
SQL | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
7.2 Q2. 如何确定 prepared statement 生效¶
A
:当发送请求到 Doris
之后,在 fe.audit.log
中找到相应的 query
请求,发现 Stmt=EXECUTE()
,说明 prepared statement
生效
Text Only | |
---|---|
1 2 3 |
|
7.3 Q3. 非主键查询能否使用到高并发点查的特殊优化¶
A
:不能,高并发点查只针对于 key
列的等值查询,且查询中不能包含 join
,嵌套子查询
7.4 Q4. useServerPrepStmts 在普通查询中是否有用¶
A
: Prepared Statement
目前只在主键点查的情况下生效
7.5 Q5. 优化器选择需要进行全局设置吗¶
A
:在使用 prepared statement
进行查询时, Doris
会选择性能最好的查询方式,不需要手动设置优化器