1.原因
在TIDB中对数据的存储是通过对key的二进制排序方式做存储的。比如一个64bit的主键就会映射为一个[0,9223372036854775807)的一个sort_map,然后对这一个巨大的sort_map做切分,切分为一个默认96M的region。在表一开始就被建立起来的时候,这个时候是只有一个region的,那么为什么不一开始就把需要的region分布好呢?比如一个int的主键,默认就分成多个region这样就解决写入问题了啊。根据TIDB博客中的说法是,TIDB作为一个通用的数据库是不会对数据的分布做任何猜测的,热点的问题可以在数据真实插入之后在根据实际情况通过PD进行调度。况且就算一开始对int的主键做了数据的切分,也不代表万事大吉了,因为只有在数据用一种离散写入方式的时候才是均匀的写入,如果数据在一开始写入的时候就是一个范围的写入,这种预先切分的方式也会产生热点。那么这个热点到底是怎么产生的呢?
根据之前提到的内容,一个表在一开始的时候都是一个region,那么在做大量数据导入的时候就会发生所有数据都插入到一个region的情况继而产生了热点的问题。在一个region写满了之后会进行分裂(如下图所示)。比如由region1分裂为region2和region3,那么既然有新的region出来了肯定就会做选举,选举出新region对应的leader。就用下面的图来说,假设region1的leader在tikv-node1上面,在发生分裂之后进行选举,因为是谁分裂谁先发起选举,所以region2和region3的leader大概率也会在TIKVnode1上面,那么这个时候就算做了分裂,因为region2、region3的leader都在tikvnode1上面,所以node1依然会承担大量的写入压力。需要等待pd根据tivk周期性的心跳信息分析判断,然后生成调度计划,最后在调度计划成功执行之后,热点的问题才能逐渐被解决。
2.解决
要解决这个问题,可以通过预分配region的方式去解决。虽然说TIDB默认情况下不会做预分配,但是不代表TIDB没有提供预分配的功能,在TIDB中可以通过下面的方式去进行预分配。
SPLIT TABLE TEST_HOTSPOT BETWEEN (0) AND (9223372036854775807) REGIONS 128;
这个案例是对一个int主键的方式做预分配,预分配了128个region。
那么对于没有主键或者非整型类主键的表如何去做呢?对于一个非int主键或者没有主键的时候,tidb会隐式的分配一个_tidb_rowid来作为这个表的主键,在没有开启SHARD_ROW_ID_BITS的情况下tidb_rowid是单调递增的。说一下shared_row_id_bits的作用,这个主要作用就是把tidb_rowid打散,一般数据写入到不同的region,比如SHARD_ROW_ID_BITS = 4表示2^4个范围,一般来说shared_row_id_bit会结合pre_split_regions一起使用,pre_split_regions表示预先做切分。不然,tidb_rowid被打散了,region没预先分配也没意义啊~ 比如:
create table t (a int, b int,index idx1(a)) shard_row_id_bits = 4 pre_split_regions=2;