一、分布式事务问题

一.一致性有几种分类

1、强一致性

系统中的某个数据被成功更新后,后续任何对该数据的读取操作都将得到更新后的值。也称为:原子一致性(Atomic Consistency)、线性一致性(Linearizable Consistency)
简言之,在任意时刻,所有节点中的数据是一样的。例如,对于关系型数据库,要求更新过的数据能被后续的访问都能看到,这是强一致性。

2、弱一致性

系统中的某个数据被更新后,后续对该数据的读取操作可能得到更新后的值,也可能是更改前的值。
但即使过了不一致时间窗口这段时间后,后续对该数据的读取也不一定是最新值。
所以说,可以理解为数据更新后,如果能容忍后续的访问只能访问到部分或者全部访问不到,则是弱一致性。
例如12306买火车票,虽然最后看到还剩下几张余票,但是只要选择购买就会提示没票了,这就是弱一致性。

3、最终一致性

是弱一致性的特殊形式,存储系统保证在没有新的更新的条件下,最终所有的访问都是最后更新的值。
不保证在任意时刻任意节点上的同一份数据都是相同的,但是随着时间的迁移,不同节点上的同一份数据总是在向趋同的方向变化。
简单说,就是在一段时间后,节点间的数据会最终达到一致状态。

二、跨界点关联Join问题

1、全局表

全局表指的是所有库都存放同一份数据,一般是一些数据字典表,全局表在Sharding-JDBC称之为广播表。

2、字段冗余

这是一种典型的反范式设计,为了避免关联JOIN,可以将一些冗余字段保存,比如订单表保存userId时,可以将userName也一并保存,这样就避免了和User表的关联JOIN了。

但是这样也存在着数据一致性的问题,比如一个人今天下单的时候,个人信息为ID=100,NAME=张三,第二天他修改了个人信息ID=100,NAME=法外狂徒,个人信息表的数据就和订单表的数据不完全一致了,因此也通向需要修改定单表的数据。

3、数据组装

直接不使用JOIN关联,分两次查询,从第一次的结果集中找出关联数据的唯一
标识,然后再次去查询,最后对得到的数据进行组装
但是进行手动组装,对于数据量很大的情况下,对CPU+内存都有较高的要求。

4、绑定表

对于相互关联的数据节点,通过分片规则将其切分到同一个库中,这样就可以直接使用SQL的JOIN 进行关联查询,Sharding-JDBC中称之为绑定表,比如订单表和用户表的绑定。

三、跨节点分页、排序、函数问题

对于跨数据节点进行分页、排序或者一些聚合函数,筛选出来的仅仅是针对当前节点,比如排序,仅仅能够保证在单一数据节点上是有序,并不能保证在所有节点上都是有序的,需要将各个节点的数据的进行汇总重新手动排序

四、全局主键避重问题

单库单表一般都是使用的自增主键,但是在切分之后每个自增主键将无法使用,因为这样会导致数据主键重复,因此必须重新设计主键。

1、UUID(无序会增加插入消耗)

UUID应该是大家最为熟悉的一种方案,优点非常明显本地生成,性能高,缺点也很明显,太长了存储耗空间,查询也非常耗性能,另外UUID的无序性将会导致InnoDB下的数据位置变动。

2、Snowflake

Twitter开源的由64位整数组成分布式ID,性能较高,并且在单机上递增

五、数据迁移、扩容问题

当业务高速发展,面临性能和存储的瓶颈时,才会考虑分片设计,此时就不可避免的需要考虑历史数据迁移的问题。一般做法是先读出历史数据,然后按指定的分片规则再将数据写入到各个分片节点中。此外还需要根据当前的数据量和QPS,以及业务发展的速度,进行容量规划,推算出大概需要多少分片。
如果采用数值范围分片,只需要添加节点就可以进行扩容了,不需要对分片数据迁移。如果采用的是数值取模分片,则考虑后期的扩容问题就相对比较麻烦。
我个人觉得按日期范围+日期范围取模分片比较合适。