主键HASH打散

云原生多模数据库Lindorm的主键HASH打散功能是指通过HASH函数将数据分散到不同的分片(Region),实现数据的分布式存储和查询,避免数据倾斜和负载不均等问题。本文介绍主键HASH打散功能的使用方法。

背景信息

Lindorm宽表引擎是基于Range分区的分布式存储引擎。通常情况下,写入Lindorm宽表引擎的数据会均匀地分布在不同的分片中。

在实际使用过程中,如果流量分布不均匀,例如访问某些数据的频率明显高于其他数据,则可能会导致数据在分片上的分布不均衡进而产生热点问题,无法充分利用分布式系统的优势。此时,您可以通过调整访问方式解决数据分布问题。但如果该问题是由于主键以某种规则生成,无法打散(例如自增主键或格式为HH:mm:ss的时间列),推荐您使用主键HASH打散功能。

主键HASH打散功能可以将上述无法打散的数据均匀地分散到不同的分片,提升系统的可扩展性。同时,支持在Range分区的基础上执行分布式查询,提高查询效率。

如果您的业务场景符合以上适用场景,那么在建表或创建二级索引时对指定主键列使用HASH算法,即可实现数据打散。

前提条件

宽表引擎为2.5.3及以上版本。如何查看或升级当前版本,请参见宽表引擎版本说明升级小版本

注意事项

  • 在主键列或索引中对指定列使用HASH算法时,HASH函数表达式必须放在最前面。例如错误用法为PRIMARY KEY( p1, hash32(p1),p2),正确的用法为PRIMARY KEY(hash32(p1), p1, p2)

  • 在主键列或索引中对某列使用HASH算法时,必须指定该列为主键列或索引列。

  • 已指定HASH算法的主键列不支持修改。

  • 使用主键HASH打散功能后,不支持使用bulkload方式导入数据。

DDL

建表

建表时,在PRIMARY KEY中设置主键列,并对指定主键列使用HASH算法。目前支持8位、32位和64位HASH算法。您可以根据主键的特点,对任意一个或多个主键列使用不同的HASH算法。

说明

HASH算法的位数越大,存储底层消耗的内存越多,存储成本越大。例如,采用32位HASH算法时,存储底层每对keyValue将额外消耗4 Bytes。

示例如下:

# 对一个主键列使用HASH算法
CREATE TABLE t1 (
    p1 bigint,
    p2 integer,
    c1 integer,
    c2 varchar,
    PRIMARY KEY(hash32(p1), p1, p2)
);

# 对多个主键列使用HASH算法
CREATE TABLE t2 (
    p1 bigint,
    p2 integer,
    c1 integer,
    c2 varchar,
    PRIMARY KEY(hash8(p1, p2), p1, p2) 
);

hash32(p1)表示对p1列使用32位HASH算法;hash8(p1, p2)表示同时对p1列和p2列使用8位HASH算法。

建二级索引

支持对索引表使用主键HASH打散功能。

说明

如果在创建主表时已对指定列使用HASH算法,但在建二级索引时未对指定列使用HASH算法,则索引表默认不会打散数据。

CREATE INDEX idx ON t1 (hash64(c1, c2), c1, c2);

查看HASH列详情

您可以查看表结构来查看HASH列详情。

DESCRIBE table t1;

返回结果:

+--------------+------------+-------------+---------+----------------+------------+
| TABLE_SCHEMA | TABLE_NAME | COLUMN_NAME |  TYPE   | IS_PRIMARY_KEY | SORT_ORDER |
+--------------+------------+-------------+---------+----------------+------------+
| test1        | t1         | hash32[p1]  | INT     | true           | ASC        |
| test1        | t1         | p1          | BIGINT  | true           | ASC        |
| test1        | t1         | p2          | INT     | true           | ASC        |
| test1        | t1         | c1          | INT     | false          | none       |
| test1        | t1         | c2          | VARCHAR | false          | none       |
+--------------+------------+-------------+---------+----------------+------------+

DML

数据写入

写入数据时,无需在SQL语句中添加HASH相关参数,系统将根据写入的数据自动生成并填充HASH值。

UPSERT INTO t1(p1, p2, c1, c2) VALUES(1, 1, 1, 'a');

数据查询

查询数据时,无需在SQL语句中添加HASH相关参数,系统将自动根据查询条件计算HASH值。但需要注意以下两点:

  • 必须指定所有已使用HASH算法的主键列的值。否则系统会因为列缺省而无法计算HASH值,导致无法定位到数据所在的分片从而查询全表。

    以t1表为例,查询示例如下:

    # 推荐的使用方式
    SELECT * FROM t1 WHERE p1=1 AND p2=1; # 系统将查询hash(p1)=hash32(1)的分片
    
    # 不推荐的使用方式
    SELECT * FROM t1 WHERE p2=1; # 未指定主键列p1的值,会导致系统无法定位至HASH值对应的分片进而查询全表
  • 对于使用了HASH算法的主键列,查询条件必须为等值查询,例如p1=2,不支持HASH列的范围查询,例如p1>1

    以t1表为例,查询示例如下:

    # 正确使用方式
    SELECT * FROM t1 WHERE p1=2 AND p2>1;
    
    # 错误使用方式
    SELECT * FROM t1 WHERE p2=1 AND p1>2 AND p1<8; # 不支持此查询方式(主键列p1设置为范围查询)

如需查看SELECT语句的执行情况,请参见EXPLAIN