Tablestore自增列的正确使用姿势

本文立题来源于Tablestore的日常线上支持,为解刚上手Tablestore的同学对“主键列自增”这个功能的使用疑惑。

 

一个日常咨询

X同学:我想修改一行数据,用UpdateRow接口,但是我的表里用了自增列,我不知道自增主键的值是什么,PrimaryKey怎么填写呢?

技术支持:您要修改这行需要先读出来,拿到主键信息,再做修改。

X同学:是用GetRow接口读吗?那GetRow的PrimaryKey怎么填写?

技术支持:没有完整的主键信息不能使用GetRow接口读,您可以尝试用GetRange加上过滤条件找到要修改的行。如果有单行读和单行修改的操作,建议在插入行的时候选择返回主键信息,做一下记录供以后操作,可以参考文档

X同学:...(内心感觉很不好用)

 

自增列-“主键列自增”,是Tablestore很有特色的一个功能,和常见的k-v存储相比,我们支持创建含有一个“自增列主键”的表。若设置某一列主键为自增列,在写入一行数据时,这一列主键无需填值,表格存储会自动生成这一主键列的值。该值在分区键上保证唯一,且严格递增。

 

要想知道自增列主键的用途,不妨了解下它被创造的背景,自增列主键最早开发是为了给某商务沟通软件用的,目标是高效支持一个IM系统,如果您正是要找IM系统的实现方案,可以直接参考Tablestore主键列自增功能在IM系统中的应用

社交IM的基本需求是:将源用户发送的消息及时、准确地更新给该目的用户。一条消息的基本元素 {接收方的用户ID,消息内容};为了保证所有的消息能够按照正确的顺序被接收端处理,添加消息ID,一条消息的结构变为 {接收方的用户ID,消息ID,消息内容},保证消息ID严格递增。

实现上以“接收方用户ID”和“消息ID”做主键,“消息内容”为属性,一条消息存为一行数据;接收端根据“接收方用户ID”和“消息ID”的范围批量获取并处理对应用户的消息。

通过GetRange接口读取最近的消息。message_id这一列PK的起始位置是上一条消息的message_id+1,结束位置是INF_MAX,这样每次都可以读出最新的消息,然后发送给客户端。代码实现参考

 

自增列分区键级别唯一,严格递增但不保证连续

分区键(第一个主键)级别唯一,从上述背景可看到,相同的“接收方用户ID”的消息在同一个分区键内,所以分区键级别唯一且严格递增的“消息ID”已经可以保证消息的顺序性。为什么不是全表唯一呢,Tablestore的底层实现决定,有“相同的分区键的行”会被放在“同一个分区”,一个分区同一时刻唯一被一台机器加载;所以简单理解“分区键级别”是同一台机器上的操作,而“表级别”则是跨机器的操作,出于性能和可扩展性保证,我们提供部分扩展操作都是分区键级别的:自增列、局部事务等。

 

综上可看,自增列的一个主流用法是:“同一个ID”有序的存储一批数据,单条或批量写入,然后批量的读取和处理。自增列的主要作用是“保序”,而不是作为唯一定位一行的主键部分的主要“身份区分”元素。一来自增列的并值不是全表唯一的,二来在写入时就开始记录自增主键的值有一定维护成本。           

 

一个用的不好的例子

  • 某用户做一个全国食品连锁店的店铺信息统计,把自增主键作为区分同名连锁店的唯一标识:

PK1

PK2

PK3

col1

col2

col3

......

北京

KFC

1578490808029000

地址:朝阳区

电话:xxx

营业时间:xxx

 

上海

M记

1578490791443000

地址:徐汇区

电话:xxx

营业时间:xxx

 

杭州

必胜客

1578490823817000

地址:西湖区

电话:xxx

营业时间:xxx

 

杭州

必胜客

1578490833204000

地址:滨江区

电话:xxx

营业时间:xxx

 

杭州的两家必胜客,插入数据时没有问题,自增的主键只填一个占位符,补充其他信息即可。存储时也没有问题,自增主键保证了主键的唯一性,但是查询信息的时候就头疼了,插入行的时候并没有保留自增列PK3的信息,没有完整的主键信息既没办法读取单行信息,也不能对单行信息进行修改。

 

同样的主键结构换个场景则适用:

  • 统计不同地区“不同品牌”连锁店的,高低峰就餐时间段不同桌型的就座率

PK1

PK2

PK3

col1

col2

......

北京

KFC

1578490808029000

桌型:两人桌

新增就餐人数:1

 

上海

M记

1578490791443000

桌型:四人桌

结束就餐人数:3

 

杭州

必胜客

1578490823817000

桌型:六人桌

新增就餐人数:5

 

杭州

必胜客

1578490823817800

桌型:六人桌

结束就餐人数:5

 

杭州

必胜客

1578490833204000

桌型:四人桌

新增就餐人数:4

 

每隔时间周期,批量获取一次某品牌不同桌型新增/结束就餐人数信息做增量处理,统计出实时的就座率。这里的自增列PK3保证了同一桌人开始就餐和结束就餐的顺序不会错乱,就座率始终为正。和上例的不同点:1.把“杭州-必胜客”看为一个“同一个ID” 2.行的属性信息是一个待处理的队列性质信息。

 

  • 如果还是要实现原来的需求店铺信息统计,那么改一下表结构使主键唯一且用户可知:

PK1

PK2

col1

col2

col3

......

北京

KFC

地址:朝阳区

电话:xxx

营业时间:xxx

 

上海

M记

地址:徐汇区

电话:xxx

营业时间:xxx

 

杭州

必胜客(西湖区)

地址:西湖区

电话:xxx

营业时间:xxx

 

杭州

必胜客(滨江区)

地址:滨江区

电话:xxx

营业时间:xxx

 

 

  • 如果只想借Tablestore的主键自增功能做一个“唯一ID生成器”的话:

建议用一张表专门生层“唯一ID”,先插入一条数据到这张表拿到返回的主键信息,再写入业务表。

PK1

PK2

col1

ID生成

1578490808029000

000

ID生成

1578490791443000

000

ID生成

1578490823817000

000

ID生成

1578490833204000

000

 

PK1

PK2

col1

col2

col3

col4

......

北京

KFC

1578490808029000

地址:朝阳区

电话:xxx

营业时间:xxx

 

上海

M记

1578490791443000

地址:徐汇区

电话:xxx

营业时间:xxx

 

杭州

必胜客(西湖区)

1578490823817000

地址:西湖区

电话:xxx

营业时间:xxx

 

杭州

必胜客(滨江区)

1578490833204000

地址:滨江区

电话:xxx

营业时间:xxx

 

不推荐这样使用自增列属性,因为若想保证全表唯一,生成ID的表的分区键是固定的。即全表只有一个分区键,落在一个分区一台机器上,且分区无法再分裂,一个分区下的数据不宜过多,应该控制在10GB以内,当行数到千万级及以上时可能就会到达瓶颈。

 

 

本文主要讲Tablestore的自增列最为普遍的使用方式,大家如果用在了更多新场景解锁了更多新姿势,欢迎进群交流讨论。钉钉群:“表格存储公开交流群”,群内提供免费的在线专家服务,欢迎扫码加入,群号:23307953

Tablestore自增列的正确使用姿势

 

 

 

 

 
 
上一篇:DOS命令:列出某目录下的所有文本文件名并重定向到某文件


下一篇:Sql_Server中如何判断表中某列是否存在