高并发点查

本文介绍云数据库 SelectDB 版高并发点查相关优化设计和使用指南,帮助您优化查询并发和响应延时。

背景

在高并发服务场景中,一般是从系统中获取整行数据。而SelectDB基于列存构建,当表较宽时,列存格式将大幅放大随机读取IO,造成查询速度下降。SelectDB的优化器和执行引擎,对于某些简单的查询(如点查询)来说过于繁重,需要SelectDB查询优化器具备规划短路径的能力来处理这样的查询。此外,SelectDB的查询入口层使用Java编写,分析和解析高并发查询SQL也会导致高CPU开销。为了解决这些问题,SelectDB引入了行存、短查询路径、PreparedStatement来解决这些问题。

行存

可以在SelectDB表中开启行存模式,但是需要消耗额外的存储空间。目前的行存实现是将行存编码后存在单独的一列中,这样做简化了行存的实现。行存模式仅支持在建表的时候开启,需要在建表语句属性(property)中指定如下属性。

"store_row_column" = "true"

在Unique模型下的点查优化

在Unique数据模型下使用行存时,开启Merge-On-Write策略以减少点查时的IO开销。当enable_unique_key_merge_on_writestore_row_column在创建Unique表的语句中被设置为开启时,对于主键的点查会采用短路径规划来对SQL执行进行优化,仅需要执行一次RPC即可执行完成查询。

例如在一个高并发点查场景下,在Unique模型中开启行存、Merge-On-Write策略。

CREATE TABLE `tbl_point_query` (
    `key` int(11) NULL,
    `v1` decimal(27, 9) NULL,
    `v2` varchar(30) NULL,
    `v3` varchar(30) NULL,
    `v4` date NULL,
    `v5` datetime NULL,
    `v6` float NULL,
    `v7` datev2 NULL
) ENGINE=OLAP
UNIQUE KEY(`key`)
COMMENT 'OLAP'
DISTRIBUTED BY HASH(`key`) BUCKETS 16
PROPERTIES (
    "enable_unique_key_merge_on_write" = "true",
    "light_schema_change" = "true",
    "store_row_column" = "true"
);
说明
  • 推荐开启enable_unique_key_merge_on_write,以方便存储引擎根据主键来进行快速点查。

  • 当条件只包含主键时,例如select * from tbl_point_query where id = 123,类似的查询会采用短路径规划来优化查询。

  • 推荐开启light_schema_change,因为主键点查的优化依赖了轻量级Schema变更中的column unique id来定位列。

  • 点查只支持单表Key列等值查询,不支持Join、嵌套子查询。where条件里需要有且仅有Key列的等值,可以认为是一种键值对查询。

使用PreparedStatement

为了减少SQL解析和表达式计算的开销,SelectDB查询入口层提供了与MySQL协议完全兼容的PreparedStatement特性(目前只支持主键点查)。若在FE开启PreparedStatement,SQL和其表达式将被提前计算并缓存到Session级别的内存缓存中,后续的查询直接使用缓存对象即可。当CPU成为主键点查的瓶颈时,在开启PreparedStatement后,将会有4倍以上的性能提升。

例如在JDBC中使用PreparedStatement

  1. 设置JDBC URL并在Server端开启PreparedStatement

    url = jdbc:mysql://127.0.0.1:9030/ycsb?useServerPrepStmts=true
  2. 使用PreparedStatement

    // use `?` for placement holders, readStatement should be reused
    PreparedStatement readStatement = conn.prepareStatement("select * from tbl_point_query where key = ?");
    ...
    readStatement.setInt(1234);
    ResultSet resultSet = readStatement.executeQuery();
    ...
    readStatement.setInt(1235);
    resultSet = readStatement.executeQuery();
    ...

开启行缓存

SelectDB中有针对Page级别的内存缓存(Page Cache),每个Page中存储的是某一列的数据,这意味着Page Cache是针对列的缓存。对于前面提到的行存,一行里包括了多列数据,缓存可能被大查询给刷掉,为了增加行缓存命中率,SelectDB单独引入了行存缓存。行缓存复用了SelectDB中的LRU Cache机制来保障内存的使用,通过指定如下的BE配置来开启。

  • disable_storage_row_cache是否开启行缓存,考虑内存开销,默认值为true,不开启行缓存。

  • row_cache_mem_limit指定Row Cache占用内存的百分比,默认值为20,代表20%内存占比。