分区索引是为了解决大宽表的存储和高并发访问问题而设计的一种新特性。创建搜索索引时可以指定数据分区策略,服务端自动将数据进行拆分并存储,查询数据时系统自动进行分区裁剪。本文介绍数据分区的策略和使用方法。
前提条件
使用场景
Lindorm搜索索引提供HASH分区和时间分区两种分区策略,您可以根据以下说明中匹配的业务场景选择合适的分区策略:
HASH分区
查询分区裁剪:当业务查询条件中始终包含某一列的等值查询时,可以考虑对该列进行HASH分区,以确保相同的值始终落入同一组分区。在查询过程中,可以实施分区裁剪,仅检索包含查询值的特定分区。例如对于设备表,若查询条件总是带有设备ID,则可以将设备ID列设置为HASH分区键。
分区存储均衡:HASH分区键的选取同时也要考虑到数据分布,如果业务选取的一级分区键存在热点,则可以结合多级HASH分区特性,在一级分区的基础上将数据更加均匀地散列在几个分区内。
时间分区
查询分区裁剪:业务数据具有时间属性,比如车联网数据、订单详情、消息日志等,且在查询的时候经常会使用时间范围,例如查询近7天、近一个月的订单数据,可以使用时间分区特性,根据查询时间范围缩小查询涉及的分片范围。
单分片存储上限控制:如果单个索引或分片存储的数据量过多,一旦数据量增长达到单分片极限,会影响写入及查询性能。如果业务索引数据量会一直快速增长,建议结合时间分区策略,控制单个分片内的数据量。
准备工作
使用分区索引前需要创建测试表,语句如下:
CREATE TABLE IF NOT EXISTS search_table (user_id BIGINT, storeId VARCHAR, goodsId VARCHAR, goodsPrice SMALLINT, orderTime BIGINT, info VARCHAR, PRIMARY KEY (user_id asc));
HASH分区
Lindorm搜索索引最多支持三级分区,HASH分区创建后不可改变。HASH分区的语法示例如下:
一级分区
创建搜索索引,默认情况下会使用宽表的主键进行HASH分区,默认设置的分区数量为搜索节点数的两倍。
CREATE INDEX IF NOT EXISTS idx USING SEARCH ON search_table (storeId, goodsId, goodsPrice);
创建搜索索引,指定按照
storeId
列进行HASH分区,分区数量为64
。其中PARTITION BY hash(storeId)
用于指定一级分区键为storeId
,partitions 64
用于指定总分区数为64
。CREATE INDEX IF NOT EXISTS idx USING SEARCH ON search_table (storeId, goodsId, goodsPrice) PARTITION BY hash(storeId) PARTITIONS 64;
说明业务在选用HASH分区时需要提前根据数据量来评估分区数,一般情况下建议一个分区数据总数在5000万到1亿之间,存储量在30 GB~50 GB之间。
多级分区
当索引总数据量较多(百亿级别以上),且一级分区键会产生不均衡数据分布时,建议使用多级分区进行优化。
要求宽表引擎为2.8.1及以上版本,搜索引擎为3.9.22及以上版本。
多级分区暂不支持和时间分区功能混用。
创建搜索索引,指定按照宽表的
storeId
和goodsId
进行HASH分区,且指定各自分区的加盐因子(salt_factor)分别为2
和4
,partitions 64
用于指定总分区数为64
。CREATE INDEX IF NOT EXISTS idx USING SEARCH ON search_table (storeId, goodsId, goodsPrice) PARTITION BY hash(storeId(salt_factor=2), goodsId(salt_factor=4)) partitions 64;
说明分区加盐因子(salt_factor)应设置为一个较小的整数,用于控制不同分区键值的散列程度,可根据以下步骤确定总分区数及各分区键的加盐因子:
根据总数量预估总分区数,再选取一个较接近
的数字作为最终的总分区数。例如总数据量为20亿,每个分区大致包含的数据量为5,000万,则大致的分区数量为 40
,此时可以选择作为总分区数。 确定总分区数为
后,建议将多个分区键的加盐因子之和设置为 6
,您可以自由分配加盐因子来控制不同分区键的散列范围。例如:示例中设置了
storeId
的加盐因子为2
,goodsId
的加盐因子为4时,相同storeId
,不同goodsId
的数据会被分配到总分区的中。若设置 storeId
的加盐因子为3
,goodsId
的加盐因子为3
,则相同storeId
,不同goodsId
的数据会被分配到总分区的中。
创建分区索引,指定二级分区键为
_id
。说明如果一级分区键会导致数据分布不均,且业务上没有合适的二级分区键,建议您将
_id
指定为二级分区键,_id对应宽表主键组合。CREATE INDEX IF NOT EXISTS idx USING SEARCH ON search_table (storeId, goodsId, goodsPrice) PARTITION BY hash(storeId(salt_factor=2), _id(salt_factor=4)) partitions 64;
时间分区
对于时间序列数据如订单数据、消息日志等,可以指定时间列,按照时间范围分区。例如按照周或者月进行分区,同一时间范围内的数据将会聚集存储,并且可以自动淘汰旧分区的数据。
创建索引,按照业务的时间列
orderTime
分区。RANGE_TIME_PARTITION_START='30'
表示从30天前开始创建分区,RANGE_TIME_PARTITION_INTERVAL='7'
表示每7天自动分区,RANGE_TIME_PARTITION_TTL='90'
表示默认保留90天的分区数据,即90天以前的数据会被自动清理。CREATE INDEX idx USING SEARCH ON search_table (storeId, goodsId, goodsPrice, orderTime) PARTITION BY RANGE time(orderTime) partitions 4 WITH ( indexState=ACTIVE, RANGE_TIME_PARTITION_START='30', RANGE_TIME_PARTITION_INTERVAL='7', RANGE_TIME_PARTITION_TTL='90' );
重要RANGE_TIME_PARTITION_START参数用于控制分区起始的时间,表示从多少天前开始创建分区。
假设orderTime的最早日期为2021年3月16日,如果业务表需要永久保留,您需要计算从2021年3月16日至创建索引当天间隔的天数,并将其设置为RANGE_TIME_PARTITION_START参数的值。起始时间设置后,若后续新增的orderTime数据小于该值,则新增的记录会被忽略。
创建索引,按照业务的时间列
orderTime
分区,从半年前开始,每1个月自动分区,默认保留半年的分区数据,分区字段单位设置为秒(s)。CREATE INDEX idx USING SEARCH ON search_table (storeId, goodsId, goodsPrice, orderTime) partition by range time(orderTime) partitions 4 with ( indexState=ACTIVE, RANGE_TIME_PARTITION_START='180', RANGE_TIME_PARTITION_INTERVAL='30', RANGE_TIME_PARTITION_TTL='180', RANGE_TIME_PARTITION_FIELD_TIMEUNIT='s' );
按照时间范围分区的参数说明如下表:
参数 | 是否必填 | 说明 |
RANGE_TIME_PARTITION_START | 是 | 表示创建索引操作前多少天开始创建分区。适用于有历史数据的场景,如果历史数据的时间戳比开始分区的时间还要小,则会报错。 |
RANGE_TIME_PARTITION_INTERVAL | 是 | 表示间隔多少天创建新分区,例如 |
RANGE_TIME_PARTITION_TTL | 否 | 表示保留多少天的分区数据,例如 |
RANGE_TIME_PARTITION_FIELD_TIMEUNIT | 否 | 表示业务指定的时间分区字段单位,默认单位为毫秒(ms)。
|
RANGE_TIME_PARTITION_MAX_OVERLAP | 否 | 如果写入的数据时间点是将来的时间,该参数表示最多允许与当前时刻的时间间隔,单位为天。不指定时则表示无限制。 |
RANGE_TIME_PARTITION_FORMAT | 否 | 表示业务指定的时间分区字段写入格式,默认为 |