分布式缓存和分布式事务

2021-02-06  本文已影响0人  LegendGo

分布式缓存

缓存选型

Memcache

memcache 提供了简单的kv cache存储,value大小为1mb
memcache 使用slab方式来做内存管理,这种方式存在一定的浪费,如果大量接近的item,建议调整memcache参数来优化每一个slab增长的ratio,可以通过设置slab_automove 和slab_reassion 开启memcache的动态/手动move slab,防止某些slab热点导致内存不足的情况下引发LRU。
每个slab包含若干个大小为1M的内存页,这些内存又被分割成多个chunk,每个chunk存储一个item。memcache在启动初始化的时候,每个slab都会分配一个1M的内存页由slabs_preallocate 完成,chunk的增长因子由-f指定,默认是1.25,初始大小为48字节

Redis

redis 有丰富的数据类型,支持增量方式的修改部分数据,比如排行榜,集合,数组等
比较常用的方式是使用 redis 作为数据索引,比如评论的列表 ID,播放历史的列表 ID 集合,我们的关系链列表 ID。
因为redis是没有内存池的,所以是存在一定的内存碎片,一般会使用jemalloc来优化内存分配,需要编译使用jemalloc库来代替glib的malloc使用。

分布式缓存集群方案

当数据量逐步增加,需要搭建多个缓存实例来支撑业务,需要将数据进行分片缓存。

Proxy

通过Proxy的方式对缓存集群进行封装,当需要插入数据/查询数据,通过Proxy的方式对缓存集群进行访问。
技术方案

一致性Hash

一致性hash是将数据按照特征值映射到一个首尾相接的hash环上,同时将节点按照IP地址或是机器名hash,映射到这个环上。
对于数据,从数据在环上的位置开始,顺时针找到第一个节点就是数据测存储节点。
余数分布式算法由于保存建的服务器发生巨大变化二影响缓存的命中率。但Consistent Hashing中,只有在环上增加服务器的地点逆时针方向的第一台服务器上的键会收到影响。

特点

Slot

redis-cluster把16384槽按照节点数量进行平均分配,由节点进行管理。对于每个key按照CRC16规则进行hash运算,把hash结果对16383进行取余,把余数发送给Redis节点。
Redis Cluster的节点之间会共享消息,每个节点都会知道哪个节点负责哪个范围内的数据槽。
当新增加节点的时候,只需要之前每个节点余处一部分槽给到新的节点即可,保证在新增加节点的时候,只有少部分数据会发生迁移。

缓存模式

数据一致性

Storage和Cache同步更新比较容易出现数据的不一致性

图片.png
不一致的原因

在Cache Aside模型中,读缓存Miss的回填操作和修改数据同步更新缓存,包括消息队列的异步补偿缓存,都无法满足"Happens Before", 会存在相互覆盖的情况

图片.png
解决方案

读写同时操作

  1. 读操作,读缓冲,缓存miss
  2. 读操作,读DB,读到数据
  3. 写操作,更新DB数据
  4. 读操作,ADD操作数据回写缓存(可以异步JOB操作,Redis可以使用SETEX操作)
  5. 写操作使用SET操作命令,覆盖写缓存,读操作,使用ADD操作回写MISS数据,从而保证写操作最新的数据不会被读操作的回写数据覆盖。

多级缓存

微服务拆分细粒度原子业务下的整合服务(聚合服务),用以提供粗粒度的接口,以及二级缓存加速,减少扇出的rpc的网络请求,减少延迟。
因此在过程中,最重要的是要保证多级缓存的数据一致性

热点缓存

对于热点缓存key,按照如下思路处理

穿透缓存

对于缓存穿透,可以参考下面的的方式进行处理

缓存技巧

Incast Congestion

如果网络中包太多,会发生Incast Congestion的问题,可以理解为,network上有很多swtich,router等,一旦一次性发一堆包,这些包会同时到达switch,这些switch会无法处理这些数据,针对这样的场景可以通过下面几个点进行考虑

其他小技巧

memcache 小技巧

redis小技巧

分布式事务

事务在单一的数据库上,是可以保证ACID,但是在多服务相互调用的时候,如果保证数据的一致性。

事务消息

如何可靠的保存消息凭证。
要解决消息的可靠存储,实际上需要解决的问题是,本地MySQL存储和message存储的一致性问题。

  1. Transactinal outbox
  2. Polling publisher
  3. Transaction log tailing
  4. 2PC Message Queue

事务消息一旦被可靠持久化,我们整个分布式事务,变为了最终的一致性。消息的消费才能保证整体业务的完整性,所以需要尽最大努力把消息送达到下游的业务消费方,只有消息被消费,整个交易才能算是完成。

Transactinal outbox

Transactinal outbox, 服务A在完成服务同时,同时记录消息数据,这个消息数据同业务数据保存在同一个数据库实例

Begin Transaction
 update a set amount= amount-1000 where user_id = 1;
 insert into msg(user_id,amount,status) values (1,1000,1);
 end transaction
commit

上面的事务保证只要支付宝被扣钱了钱,消息一定会被保存下来,当上述事务提交成功后,会通知下游服务,当下游服务完成的时候,则会删除该条消息。

Polling publisher

Polling publisher,我们可以定时轮询msg表,把status =1的消息通通拿出来消费,可以按照自增ID排序,保证顺序消费。在这里我们可以独立出一个task服务,把拖出来消息publish给消息队列,Balance服务自己来消费队列,或者直接rpc 发送给balance服务

Transaction log tailing

Transaction log tailing,上述保存消息的方式使得消息数据和业务数据紧耦合在一起,从架构上看并不优雅,容易诱发其他问题。
在一些业务场景中,可以通过主表被canal订阅的方式来进行处理。使用canal订阅后,是实时流式消费数据,在消费者balance或者balance-job必须努力送达到。

幂等

在分布式事务中,需要重点处理的是消息重复投递的问题,如果相同的消息被投递了两次,那么会增加数据重复的问题,可以考虑下面两个方式

2PC Message Queue

两阶段提交协议(Two Phase Commitment Protocol

Seata 2PC

图片.png

TCC

TCC 是 Try、Confirm、Cancel 三个词语的缩写,TCC 要求每个分支事务实现三个操作:预处理 Try、确认 Confirm、撤销 Cancel。

上一篇下一篇

猜你喜欢

热点阅读