同样沈主席分享,分别从如何保证Session一致性、主从、主主一直、缓存一致以及时间问题待分享的冗余数据的一致性、多库事务的一致性、库存扣减的一致性、消息时序的一致性进行讨论。
单机部署必然不会出现保证一致性的系统设计,一致意味着至少大于等于2台服务节点,才有必要考虑双机数据一致的问题。数据多种类型,如上提到的session数据、db集群数据、缓存与db的数据。从具体到抽象,先分别看看各种数据类型在保证一致性方面的经典方式,再通而总之,看看各数据保证一致性是否有通用的设计思路。
Session一致性
多个web服务,nginx请求分发默认是轮训方式,若用户1访问若web1,而后续访问将请求分发到web2上,则导致web2上无用户session信息,系统提示请重新登录系统。接着web3、web4同理从而导致用户不断重新登入。
那多web节点间如何保证session数据共享且一致呢?
方案一、同步
Tomcat支持session共享的配置,百度一搜N多解决方案,tomcat自带的session共享配置,或是tomcat依赖redis实现session共享配置。
优点:代码0侵入,无需改动任何代码
缺点:若集群扩容不断增加节点,则导致同步实例越来越多,增加扩容成本。
方案二、端
将session信息不存到web server上,而是存到端上即浏览器中。这就使得使用同一浏览器进行用户请求到不论哪台web,总能获得用户session信息。
缺点:1、浏览器访问web请求实体将带入更多的session 信息,增大传输数据。
2、Session数据参与网络传输,需考虑保证数据安全性,防止窃取篡改。
方案三、保证同一用户id落到同一台web,则每次都能拿到用户session(常用)
具体实现:nginx利用uid做哈希取模即可。本方案需考虑uid在web上的分布问题,保证uid取模后,各web承载的uid请求基本持恒,避免出现某几台web高处理量,其他闲置的情况。一般只要uid生成方式均匀(单偶、数据间隔),数据都能保证取模后均匀分布(同db分表原理)
Nginx采用ip做哈希也可以。
方案四、云(常用)
新增一个缓存服务,专门用于存储(uid,session)数据。
优点:充分保证web和server的无状态、透明性,易于扩容操作。
缺点:DB用户info修改,则需考虑缓存与db的同步。
主从一致性
Mysql主从间同步机制:
通过binlog(按顺序记录主crud操作),从中以单线程记录执行binlog,从而保证从与主数据的一致。
单线程写(串行),如何提高从同步效率?Binlog中记录多个db的执行记录,按照不同db划分,使得matser中db1、db2、db3的binlog按库划分执行,从而提高binlog执行效率。阿里云的数据库同步服务DTS(底层canal)则是将DTS伪装成一个sql slave,基于上述方式,拿到主的binlog快速同步到从中。
写立即读问题:
从经验数据看来,主从间同步有大致500ms的延迟,500ms之内写完立即读,则会出现读不到或读到脏数据问题。
保证主从一致方式:
方案一、强制读写主库
优点:强制读写均在master上,则不存在读从时,主从数据不一致(binlog执行的时间差)。
缺点:主库的读压力增加,考虑使用缓存减小主库读请求的压力。(实则按下葫芦起了瓢,又需考虑缓存与db的数据一致。这可根据业务实际情况考虑是否适用)
方案二、DBProxy
在DBProxy层面控制读落到从库还是主库。具体实现是通过记录500ms内有写请求的key,500ms内对该key有读请求时,则将读请求落到主库上,保证读取最新数据。若无,则落到从库即可。
主主一致性
主主同步问题:
数据同步过程:M1中写入5,在尚未同步到M2之前,M2写入一条新的5,则导致M1 M2分别把自己的5同步给对方,出现主键冲突,M1 M2中各自少了一条记录。
方案一:同步长,不同初始值
即M1中分布1357,M2中分布2468,同步长,不同初始值,同步则不会出现上述丢数据问题。
缺点:同一时刻库中数据非全量,扩容非任意时间随时可进行。
方案二、id不采用自增,使用程序生成key做主键
方案三、DNS探测(后续补充)
缓存一致性
读流程:
先读cache,无再读db
将db中读到数据put进cache,然后返回给service。
写流程:
写redis、写db
问题1:写redis时,key已存在,是更新还是淘汰?
方法论:如果新的key值能很方便快捷获取到对应value,则直接更新redis即可。如果获取key对应的新value需要耗费多次查询db和内存计算,则直接淘汰掉该key,等待下次重新读取写入即可。一般采用淘汰机制多余更新。
问题2:写的时序性,先写redis还是db?方法论:由业务场景决定。等同于分布式事务case,商家id关联orderid,与买家id关联orderid的两个索引表,谁先更新,谁后更新。同理
方案一、写db,淘汰redis中对应key,采用schedule定时任务将数据定时写入redis中。
方案二、DTS 监测DB有写入,则同步更新redis。
总结
都是由于冗余而带来的多份冗余服务间同步、一致、读写先后等问题,但因数据类型不同,好像解决方案上各有千秋,笔者未看出如上次分享中高并发-、高可用中均通过扩容、加集群,心跳检测等保证集群间服务可用性探测等通用解决方案。毕竟加机器千篇一率,加完机器后的问题处理思路各有千秋。
ps.如果你看出一致性解决方案的公用处理方法,忘不吝赐教。