全部产品
云市场

STR_HASH

更新时间:2018-11-05 10:15:43

使用要求

  • 拆分键的类型必须是字符串类型(CHAR/VARCHAR),不接受其它类型;
  • STR_HASH的参数在建表后就不能再调整;
  • DRDS 实例的版本必须是 5.3.5 及其以上的版本。DRDS 版本说明请参考文档版本说明

函数定义

  • 拆分函数定义
    • STR_HASH( shardKey [, startIndex, endIndex [, valType [, randSeed ] ] ] )
  • 功能说明
    • 通过指定字符串的开始位置下标与结束下标,以截取拆分键的字符串的某段子串,然后将其作为字符串(或整数)输入进行分库分表的路由计算,计算具体的物理分片
    • 是专用于字符串类型的拆分函数
  • 参数说明
    • shardKey
      • 拆分键列的列名
    • startIndex 与 endIndex
      • startIndex: 目标子串的开始位置的下标(从0开始算起,即原字符串的第1个字符的下标用0表示)
      • endIndex: 目标子串的结束位置的下标 + 1(从0开始算起,即原字符串的第1个字符的下标用0表示)
      • 这两参数不填,都会取默认值-1,即不做任何截取
      • 使用时要注意以下几种的取值情况:
        • 当 startIndex == j && endIndex = k (j>=0, k>=0 ,k>j)时
          • 表示截取原字符串的[ j, k ) 区间的字符串作为子串
          • 例如,
            • 对于字符串 ABCDEFG, 子串区间 [1, 5) 的值是 BCDE
            • 对于字符串 ABCDEFG, 子串区间 [2, 2) 的值是 ‘’
            • 对于字符串 ABCDEFG, 子串区间 [4, 100) 的值是 EFG
            • 对于字符串 ABCDEFG, 子串区间 [100, 105) 的值是 ‘’
        • 当 startIndex == -1 && endIndex = k (k>=0)时
          • 表示截取原字符串最后k个字符作为子串,原字符串不足k个字符则直接获取整个字符串;
        • 当 startIndex = k && endIndex == -1 (k>=0)时
          • 表示截取原字符串开头k个字符作为子串,原字符串不足k个字符则直接获取整个字符串;
        • 当 startIndex == -1 && endIndex == -1 时
          • 表示不做任何截取,子串与原字符串完全一致。
    • valType
      • 表示截取后的子串在计算分库分表时所使用的类型(字符串或整数),目前仅可取值为0或1
      • 这参数不填,取默认值0,即以字符串类型处理
      • 当 valType = 0 时,表示DRDS将截取后的子串当作字符串类型来计算路由
      • 当 valType = 1 时,表示DRDS将截取后的子串当作整数类型来计算路由(子串面值的整数不能大于9223372036854775807,也不支持浮点数
    • randSeed (仅当 valType = 0 时 才可以设置)
      • 当子串以字符串类型来计算路由的哈希值时DRDS所使用的随机种子的值,通常不用需要填写
      • 这参数不填,取默认值31,可取其他值,如131,13131,1313131,…….等
      • 该参数仅用于当使用默认值随机种子(randSeed=31)的STR_HASH在业务实际场景中出现路由不均衡的情况时进行适调整,达到用哈希均衡数据的目的(注意,调整后所有数据要做人工ReHash)

使用示例

假设 order_id 的类型为 VARCHAR(32),现在业务想order_id作为拆分键,计划分4个库,分8个表

  • 示例1: 如果想使用 order_id 的最后4位的字符串作为整数来计算分库分表路由,则可以用以下的建表SQL
    1. create table test_str_hash_tb (
    2. id int NOT NULL AUTO_INCREMENT,
    3. order_id varchar(30) NOT NULL,
    4. create_time datetime DEFAULT NULL,
    5. primary key(id)
    6. ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    7. dbpartition by STR_HASH(`order_id`, -1, 4, 1)
    8. tbpartition by STR_HASH(`order_id`, -1, 4, 1) tbpartitions 2;
  • 示例2: 如果想截取 order_id 的第3个字符(即starIndex=2)与第7个字符(即endIndex=7)之间子串来计算分库分表路由,则可以用以下的建表SQL
    1. create table test_str_hash_tb (
    2. id int NOT NULL AUTO_INCREMENT,
    3. order_id varchar(30) NOT NULL,
    4. create_time datetime DEFAULT NULL,
    5. primary key(id)
    6. ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    7. dbpartition by STR_HASH(`order_id`, 2, 7)
    8. tbpartition by STR_HASH(`order_id`, 2, 7) tbpartitions 2;
  • 示例3: 如果想截取 order_id 的前5个字符串作为子串来计算分库分表路由,则可以用以下的建表SQL
    1. create table test_str_hash_tb (
    2. id int NOT NULL AUTO_INCREMENT,
    3. order_id varchar(30) NOT NULL,
    4. create_time datetime DEFAULT NULL,
    5. primary key(id)
    6. ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    7. dbpartition by STR_HASH(`order_id`, 5, -1)
    8. tbpartition by STR_HASH(`order_id`, 5, -1) tbpartitions 2;

使用注意

  • STR_HASH的参数在建表后就不能再调整
  • 使用STR_HASH做拆分的表仅适用于点查场景,如果业务中范围查询,则会接直接触发全表扫描,导致慢查询

适用场景

  • 实现一个分表(或分库)只对应一个拆分表键的取值(字符串类型)的精准路由效果
    • 例如,某个互联网金融应用是按年月(YYYYMM)分库,然后按订单号分表,该应用的订单号有个特点,就是订单号的最后3位字符串是一个整数,其取值范围是000~999。该应用的需求是需要在一个物理分库内,要将订单号后3位的每一个数值只单独路由到一个物理分表。那么,该应用分库采用YYYYMM,然后分表采用拆分函数STR_HASH,每个库1024个分表,就可以达到效果。具体的SQL语句如下:
      1. create table test_str_hash_tb (
      2. id int NOT NULL AUTO_INCREMENT,
      3. order_id varchar(30) NOT NULL,
      4. create_time datetime DEFAULT NULL,
      5. primary key(id)
      6. ) ENGINE=InnoDB DEFAULT CHARSET=utf8
      7. dbpartition by YYYYMM(`create_time`)
      8. tbpartition by STR_HASH(`order_id`, -1, 3, 1) tbpartitions 1024;
      应用采用这样建表SQL,原因是分表的字符串截取后3位并转换为整数(整数范围是000~999)后再取模做分表路由(共1024个分表),其路由结果能保证每一个物理分表只对一个拆分建的取值。而原来DRDS默认拆分函数HASH无法达到这样的效果,是因为字符串经过hashCode计算后的整数是不可预知的,有可能会出现一个物理分表要对应多个不同拆分建的取值。
  • 典型的点查场景
    • STR_HASH 拆分函数适用于使用字符串类型作为拆分键并且是绝大部分都是点查的场景,如根据ID查交易订单、物流订单等。

常见疑问

  • 问题: dbpartition by STR_HASH(order\_id) 与 dbpartition by HASH(order\_id) 有什么不同?
    • 答:两者虽然都是直接根据字符串取值做分库分表的哈希路由,但是两者的分库分表的路由算法实现不一样。前者除了支持用户建表时自行设定截取子串相关参数外,在根据字符串的哈希值计算分库分表路由时是基于UNI_HASH算法,而后者是只对字符串的哈希值做简单取模。