本章节主要为您介绍表格存储表设计的最佳实践。

说明 关于表格存储索引选择的最佳实践,参见表格存储存储和索引引擎详解

主键设计——数据散列

  • 为什么需要数据散列
    数据散列是分布式数据系统中的通常要考虑的问题,散列的目的是让数据分布更均匀,避免热点。假设数据分布不均匀,会出现以下问题:
    • 数据写入和读取能力受限于单个分区的能力,或者是单机能力,存在明显瓶颈。
    • 在某些数据处理场景下,热点或者数据分布不均会导致明显的长尾效应,拖慢整体速度。
    • 某个数据系统或者模块往往仅仅是整个业务链路上的一环,热点会拖慢整个上下游的速度。

    通常来讲,分布式数据库系统中,理想的数据和负载情况是:数据均匀分布,水平方式切分为很多分区,分布在不同机器上,读写压力也水平分散,每个请求的压力仅覆盖局部的一小部分,而不是整体。这种模式下完全水平扩展,业务压力增加,只需要增加机器资源即可。

    在业务设计上,应当尽量避免会导致数据热点的设计,在未来负载可支撑的情况下兼顾业务需求。有时业务需求会与数据均匀相矛盾,例如要按照时间全局有序的查询整个表最近写入的数据,那就与数据写入分散的原则有一些冲突。如果要让新写入的数据都集中在一起,系统就存在扩展瓶颈,那么在当前以及未来能否支撑这样的负载,未来系统的最大的写入TPS可能是多少,这些就是业务架构师需要考虑的问题。如果这种模式无法支持未来的负载,就意味着必须更改设计,否则是为当下或者将来埋坑。

    另一方面辩证的来看,如果业务需求很低,例如对于表格存储的TPS/QPS都在1000以下,整体数据在10GB下,且未来也不会有大的增长,那么热点问题也不是一个需要花太多精力考虑的问题,这样的负载单分区完全可以支撑。实际上,单分区也可以达到几万行/秒的写入,但是架构设计上不要依赖单分区能力,尽量控制在很安全的水平内。

  • 常见热点问题

    下表一个常见的热点案例。这是一个简单的监控场景数据表,每次存储某个机器某个时间的一些指标值。这个表中主键有两列,分别是Timestamp和MachineIp,

    这个表设计有很明显的热点问题,每次数据写入都是在表的末尾追加数据,而数据是按照分区键的范围进行分区的,也就是每次数据写入都会写入最后一个分区,而无法把写入负载平衡到多台机器上。这种情况下,单分区的写入能力就是整个表的写入能力上限,更重要的是,一旦发生热点,无法通过分片分裂来平衡负载,因为写入压力总是在写尾部。

    • 解决方法一: 合理设置分区键

      针对上面例子中的热点问题,可以把MachineIp放到主键列的第一列,Timestamp放到第二列。这样就把写入压力按照Machine这个级别进行了分散,即写入的是每个Machine的尾部,大部分情况下也足够分散了。

      类似MachineIp这种分区键是很常见的一种分区方式,即把某个业务上比较分散的Key放到第一列,例如UserId,DeviceId、OrderId等等。这种模式只要这个Key本身比较分散,一般无太大问题。

      说明 有一种局部热点情况,假设10.10.0.0/16这个网段的机器写入量很大,而表格存储是按照分区键的一个范围进行分区,刚好这些机器又都分在一个分区内,会不会产生热点呢?如果写入压力超过或接近单分区的上限,确实是一个热点,但是表格存储具备自动负载均衡的能力,会自动将这个分区进行切分(Split),使得压力平均到两个分区上,如果仍不够会继续进行切分。
    • 解决方法二: 拼接MD5
      上面的例子中,也可以通过拼接MD5的方式将IP本身的顺序打散,这种方式也较为常用。例如对IP计算,然后在IP前面接的前4位,如下图:

      这种方式可以避免某个IP段的热点,同时在结构上具备一定的模式,即以16进制字符串开头,这样有利于系统进行预分区。

  • 主键顺序建议

    上面的例子中,很多用户以Timestamp作为第一个主键列,希望数据全局按照时间排序,这样可以按照时间范围进行查询,但是这种方式导致了尾部热点,给系统能力扩展带来了瓶颈。

    您可以通过以下方法解决对顺序性的要求。
    • 方法一:业务设计上避免对全局顺序性的要求,改为局部顺序性,例如上面的例子,在某个MachineIp下,数据仍是按照Timestamp有序的。即先通过一个字段来打散数据,再按照某种顺序查询。
    • 方法二:业务上采用分桶的方式,分散写入压力,在读取时从每个桶查询再合并。例如采用16个桶,每次写入时把时间对16取模,余数放到第一个主键列(0到15),时间放到第二个主键列。在读取时,从每个桶读取后合并即可。这种方式可以把压力分散到不同的桶上,能有多么分散取决于有多少个桶。在读取时需要对所有桶进行读取,可以采用并行读取的方式进行加速。
    • 方法三:表上仍然采用均匀的方式写入数据,给表建立一个多元索引,通过多元索引进行Timestamp等字段的有序查询。多元索引在数据分布上本身会进行一次散列,分成多个分片,查询时自动从多个分片合并数据。

其它主键设计建议

  • 同一个分区键(分区键为第一列主键列)下数据不宜过多,例如10GB内(无硬性限制),因为相同分区键的行无法再进行分裂。这里相同分区键,指定的是对于第一列主键列的某个确定的值。对应上文的例子,即某个机器下的数据尽量不要超过10GB。
  • 主键列的长度限制为1KB,尽可能使用较短的主键,有利于加快查询速度。

属性列设计

  • 表格存储支持宽行,即一行可以非常宽,例如几十万个属性列。但是很宽的行,如果一次性读取,可能会读不出(超时),需要指定列或者分页读取某些列。因此,原则上不太建议非常宽的行(万列以上),可能会使某些功能受限。
  • 属性列有2MB的限制,如果要保存的数据超过这一限制,需要把数据拆分成多列保存。

专家服务

表格存储提供专业的免费的技术咨询服务,欢迎加入我们的钉钉讨论群(搜索钉钉群号『11789671』或群名称『表格存储公开交流群』)。