MySQL · myrocks · collation 限制

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: 背景MyRocks中的数据是按索引列以memcmp方式进行排序的。对于一些数字类型,需要进行转化才能直接通过memcmp进行比较, 例如有符号数在计算机中是用补码表示的,那么如果负数和正数直接按字节比较,结果负数会比正数大,实际存储时会将符号会反转存储,读取时再转化回来。

背景

MyRocks中的数据是按索引列以memcmp方式进行排序的。对于一些数字类型,需要进行转化才能直接通过memcmp进行比较, 例如有符号数在计算机中是用补码表示的,那么如果负数和正数直接按字节比较,结果负数会比正数大,实际存储时会将符号会反转存储,读取时再转化回来。对于字符类型,处理更加复杂,涉及到字符集的转换。 记录格式可以参考[1], [2]

MyRocks索引字段如果包含字符类型,默认只支持binary collation,binary、latin1_bin、 utf8_bin其中的一种

## Error
create table t1(c1 int primary key, c2 varchar(10) unique) engine =rocksdb character set gbk;
ERROR 3046 (HY000): Unsupported collation on string indexed column test.t1.c2 Use binary collation (binary, latin1_bin, utf8_bin).

## Error
create table t1(c1 int primary key, c2 varchar(10) unique) engine =rocksdb character set utf8 collate utf8_general_ci;
ERROR 3046 (HY000): Unsupported collation on string indexed column test.t1.c2 Use binary collation (binary, latin1_bin, utf8_bin).

## OK
create table t1(c1 int primary key, c2 varchar(10) unique) engine =rocksdb character set utf8 collate utf8_bin;
Query OK, 0 rows affected (0.00 sec)

通过设置rocksdb_strict_collation_check参数为OFF可以跳出binary collation的限制

set global rocksdb_strict_collation_check=OFF;

## OK
create table t1(c1 int primary key, c2 varchar(10) unique) engine =rocksdb character set gbk;
Query OK, 0 rows affected (0.01 sec)

问题

MyRocks和InnoDB一样支持covering index. MyRocks在使用二级索引查询的时候,应尽量使用covering index, 因为MyRocks回表通过主键随机查询数据的开销比较大。

例如以下场景,idx1作为convering index被使用


create table t1(c1 int primary key, c2 varchar(10) unique) engine =rocksdb character set utf8 collate utf8_bin;

insert into t1 values(1,'ab');
insert into t1 values(2,'cd');

## covering index
explain select c2 from t1 where c2='ab';
+----+-------------+-------+-------+---------------+------+---------+-------+------+-------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref   | rows | Extra       |
+----+-------------+-------+-------+---------------+------+---------+-------+------+-------------+
|  1 | SIMPLE      | t1    | const | c2            | c2   | 33      | const |    1 | Using index |
+----+-------------+-------+-------+---------------+------+---------+-------+------+-------------+

然而设置set global rocksdb_strict_collation_check=OFF;在某些情况下会导致我们无法使用covering index.

set global rocksdb_strict_collation_check=ON;

## Error
create table t1(c1 int primary key, c2 int, c3 varchar(10), key idx1(c2,c3)) engine =rocksdb  character set utf8 collate utf8_general_ci;
ERROR 3046 (HY000): Unsupported collation on string indexed column test.t1.c3 Use binary collation (binary, latin1_bin, utf8_bin).

set global rocksdb_strict_collation_check=OFF;

## OK
create table t1(c1 int primary key, c2 varchar(10) unique) engine =rocksdb character set utf8 collate utf8_general_ci;
Query OK, 0 rows affected (0.00 sec)

insert into t1 values(1,1,'ab');
insert into t1 values(2,2,'cd');


insert into t1 values(1,'ab');
insert into t1 values(2,'cd');

## non-covering index
explain select c2 from t1 where c2='ab';
+----+-------------+-------+-------+---------------+------+---------+-------+------+-------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref   | rows | Extra |
+----+-------------+-------+-------+---------------+------+---------+-------+------+-------+
|  1 | SIMPLE      | t1    | const | c2            | c2   | 33      | const |    1 | NULL  |
+----+-------------+-------+-------+---------------+------+---------+-------+------+-------+
1 row in set (0.00 sec)

原因

MyRocks二级索引由于collation的关系导致查询没有走covering index. MyRocks中索引列需要转化为memcomparable的形式,转化分为以下三种情况

  • 1) 直接转换,不需要存储额外信息

    例如 binary、latin1_bin、 utf8_bin三种collation可以直接转换

    这种情况二级索引列数据可以完整的从二级索引中取到,不影响covering index使用

  • 2) 间接转换,需在value中增加unpack_info

    例如latin1_general_ci,latin2_general_ci, ascii_general_ci,greek_general_ci等collation,具体可以参考函数rdb_is_collation_supported

    这种情况二级索引列数据可以从key和unpack_info中解析取到,也不影响covering index使用

create table t1(c1 int primary key, c2 varchar(10) unique) engine =rocksdb character set latin1 collate latin1_general_ci;

insert into t1 values(1,'ab');
insert into t1 values(2,'cd');

## covering index
explain select c2 from t1 where c2='ab';
+----+-------------+-------+-------+---------------+------+---------+-------+------+-------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref   | rows | Extra       |
+----+-------------+-------+-------+---------------+------+---------+-------+------+-------------+
|  1 | SIMPLE      | t1    | const | c2            | c2   | 13      | const |    1 | Using index |
+----+-------------+-------+-------+---------------+------+---------+-------+------+-------------+
1 row in set (0.00 sec)
  • 3) 无法转换

    除1,2两种情况外的collation, 例如utf8_general_ci

    此时从二级索引中获取不到key的完整信息,需要从主键索引上获取,因此不能走covering index

create table t1(c1 int primary key, c2 varchar(10) unique) engine =rocksdb character set utf8 collate utf8_general_ci;

 insert into t1 values(1,'ab');
 insert into t1 values(2,'cd');

## non-covering index
explain select c2 from t1 where c2='ab';
+----+-------------+-------+-------+---------------+------+---------+-------+------+-------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref   | rows | Extra |
+----+-------------+-------+-------+---------------+------+---------+-------+------+-------+
|  1 | SIMPLE      | t1    | const | c2            | c2   | 33      | const |    1 | NULL  |
+----+-------------+-------+-------+---------------+------+---------+-------+------+-------+
1 row in set (0.00 sec)

此时的数据获取路径如下

0  myrocks::ha_rocksdb::convert_record_from_storage_format 
1  myrocks::ha_rocksdb::get_row_by_rowid
2  myrocks::ha_rocksdb::get_row_by_rowid
3  myrocks::ha_rocksdb::read_row_from_secondary_key 
4  myrocks::ha_rocksdb::index_read_map_impl
5  myrocks::ha_rocksdb::index_read_map
6  handler::index_read_idx_map
7  handler::ha_index_read_idx_map
8  join_read_const
9  join_read_const_table
10 make_join_statistics
11 JOIN::optimize
12 mysql_execute_select
13 mysql_select
14 handle_select
15 execute_sqlcom_select
16 mysql_execute_command

转换的具体实现可以参考函数Rdb_field_packing::setup

MyRocks从索引读取数据时,不能仅通过keyread_only来判断是否可以使用covering index, 还需要判断是否存在collation列数据转换的问题,如果访问的列无法转换就不能使用covering index.

MyRocks会在value中存储covered_bitmap,表示索引列是否可以转换, read_row_from_secondary_key/secondary_index_read读取时会根据covered_bitmap来决定是否能使用covering index

bool Rdb_key_def::covers_lookup(TABLE *const table,
                                const rocksdb::Slice *const unpack_info,
                                const MY_BITMAP *const lookup_bitmap) const {
  DBUG_ASSERT(lookup_bitmap != nullptr);
  if (!use_covered_bitmap_format() || lookup_bitmap->bitmap == nullptr) {
    return false;
  }

  Rdb_string_reader unp_reader = Rdb_string_reader::read_or_empty(unpack_info);

  // Check if this unpack_info has a covered_bitmap
  const char *unpack_header = unp_reader.get_current_ptr();
  const bool has_covered_unpack_info =
      unp_reader.remaining_bytes() &&
      unpack_header[0] == RDB_UNPACK_COVERED_DATA_TAG;
  if (!has_covered_unpack_info ||
      !unp_reader.read(RDB_UNPACK_COVERED_HEADER_SIZE)) {
    return false;
  }

  MY_BITMAP covered_bitmap;
  my_bitmap_map covered_bits;
  bitmap_init(&covered_bitmap, &covered_bits, MAX_REF_PARTS, false);
  covered_bits = rdb_netbuf_to_uint16((const uchar *)unpack_header +
                                      sizeof(RDB_UNPACK_COVERED_DATA_TAG) +
                                      RDB_UNPACK_COVERED_DATA_LEN_SIZE);

  return bitmap_is_subset(lookup_bitmap, &covered_bitmap);
}

总结

MyRocks在字符collation上的限制需要引起我们关注,使用不当会影响查询效率。

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
1月前
|
存储 人工智能 搜索推荐
详解MySQL字符集和Collation
MySQL支持了很多Charset与Collation,并且允许用户在连接、Server、库、表、列、字面量多个层次上进行精细化配置,这有时会让用户眼花缭乱。本文对相关概念、语法、系统变量、影响范围都进行了详细介绍,并且列举了有可能让字符串发生字符集转换的情况,以及来自不同字符集的字符串进行比较等操作时遵循的规则。对于最常用的基于Unicode的字符集,本文介绍了Unicode标准与MySQL中各个字符集的关系,尤其详细介绍了当前版本(8.0.34)默认字符集utf8mb4。
|
6月前
|
SQL 关系型数据库 MySQL
【MySQL异常解决】MySQL执行SQL文件出现【Unknown collation ‘utf8mb4_0900_ai_ci‘】的解决方案
【MySQL异常解决】MySQL执行SQL文件出现【Unknown collation ‘utf8mb4_0900_ai_ci‘】的解决方案
207 0
|
SQL 关系型数据库 MySQL
MySQL执行SQL文件出现【Unknown collation ‘utf8mb4_0900_ai_ci‘】的解决方案
MySQL执行SQL文件出现【Unknown collation ‘utf8mb4_0900_ai_ci‘】的解决方案
MySQL执行SQL文件出现【Unknown collation ‘utf8mb4_0900_ai_ci‘】的解决方案
|
SQL 关系型数据库 MySQL
MYSQL中LIMIT语句(限制语句)
记录下MYSQL中LIMIT语句的基本使用
141 0
MYSQL中LIMIT语句(限制语句)
|
关系型数据库 MySQL
MySQL练习4——查询结果限制返回行数
MySQL练习4——查询结果限制返回行数!
|
关系型数据库 MySQL 数据库
nivicat复制mysql数据库[Err] [Dtf] 1273 - Unknown collation: 'utf8mb4_0900_ai_ci'错误
问题的原因是两个数据库待转移的表的字符格式不能被被转移数据库识别。例如mysql8的utf8mb4_0900_ai_ci格式再mysql5中就不支持。就需要更改数据库字符编码和各个varchar等字段的编码方式
180 0
nivicat复制mysql数据库[Err] [Dtf] 1273 - Unknown collation: 'utf8mb4_0900_ai_ci'错误
|
关系型数据库 MySQL 数据库
mysql报错Unknown collation: utf8mb4_0900_ai_ci
在我们使用高版本mysql的时候,比如mysql8,到mysql5.7 恢复就报错,比如下面这样的错误:
mysql报错Unknown collation: utf8mb4_0900_ai_ci
|
关系型数据库 MySQL 数据库
mysql导入报错 [Err] 1273 – Unknown collation: ‘utf8mb4_0900_ai_ci’
mysql导入报错 [Err] 1273 – Unknown collation: ‘utf8mb4_0900_ai_ci’
mysql导入报错 [Err] 1273 – Unknown collation: ‘utf8mb4_0900_ai_ci’
|
存储 关系型数据库 MySQL
MySQL原理 - InnoDB表的限制
MySQL原理 - InnoDB表的限制
|
SQL 关系型数据库 MySQL
Mysql常用sql语句(6)- limit 限制查询结果的条数
Mysql常用sql语句(6)- limit 限制查询结果的条数
1371 0
Mysql常用sql语句(6)- limit 限制查询结果的条数

相关产品

  • 云数据库 RDS MySQL 版