一致性设计的思考
一致性指数据的多个副本内容保持相同。一致性的处理在日常的开发中无处不在,为了适当的处理,需要想明白不一致是怎么产生的。
副本的由来:
1、获取数据后,就会产生副本。
不论你是查数据库、查缓存、rpc调用。你拿到的值已经是副本了。
2、分布式事务的状态,在多个系统或节点中作为了副本。
3、数据进行主从备份。
进行一致性相关进行设计时,需要结合业务特点,将数据的一致性问题解决至满足业务即可。
读取到历史版本的数据内容,并不是错误,只表示数据曾经的一个时刻为此值。对于查询&展示的逻辑,常常不需要额外处理。但如果查询到数据,决定了后续操作,并会对数据修改,就要谨慎处理了。
接下来观察产生不一致的行为特点,并列举一些常用的处理方式:
1、并发下,对数据及副本的读写,有交叉
串行执行;
对读取实时性要求高的查询,请求主库
加锁
只操作单个redis Key(一种特殊的用法:将需要操作的标识信息,追加到一个list值中,操作就排好执行次序了)
限制redis Key使用相同slot,并编写lua原子程序
局部串行;
开启数据库事务(注意:常用的可重复读隔离级别下,快照读切换为当前读,有可能幻读。有需要,可强制当前读(for update));
2、对数据及副本的写操作,不完整
不重要的操作靠后执行;
把容易失败的操作,最先执行,通知型的操作、有过期时间的缓存,最后处理。
双节点,争抢执行
触发其它服务节点(mq/rpc),与自己一同执行。谁争抢到了谁执行
可靠消息;
在业务数据库中,创建一个事务消息表,将发送消息的动作,作为数据的写入,与事务一同执行。并在执行后,进行补偿发送。(消息表的数据可能删除,也可能修改状态后保留)
由mq服务提供事务消息的能力,在客户端与服务端之间进行prepare/commit/rollback。服务端会在客户端长时间无动作时,主动询问事务状态。
由于mq的异步、至少消费一次的特点,可能会加重交叉读写的问题,需要处理好次序、幂等。
二次写+恢复任务
在各个副本预写(分布式存储系统中,需要半数以上。准事务逻辑,需要全部副本,只两方时,只由发起方维护预写即可),预写完成后标记完成(业务可能有失败状态)。恢复任务定时捞取预写状态数据,幂等重试更新其它副本or查询其它副本,更新为完成或失败。