跳至内容
Redis与数据库双写如何保证数据一致性

Redis与数据库双写如何保证数据一致性

在多线程并发读写的情况下,如何处理这个问题,有两种不同的情况需要讨论:

  1. 先操作Redis,后操作数据库。
  2. 先操作数据库,后操作Redis。

对于数据一致性又分强一致性和最终一致性:

  • 强一致性需要保证操作的原子性,保证原子性的则需要加锁,这样会影响并发操作的性能;
  • 最终一致性则无法保证所有查询操作能读取到更新后的数据,但是性能不会受到影响,代价较低。

在高并发的场景下,我们优先考虑性能,这里仅讨论数据的最终一致性,下面根据不同的操作过程进行详细的讨论。

img

1. 先操作Redis

更新Redis的方式有两种,直接删除数据和在原数据的基础上更新数据,这两种方式更新数据的操作会比删除数据的操作更加繁琐,采用直接删除旧数据效率会更高。整体的操作流程如下:

img

1.1 数据更新流程

  1. 线程1 更新(删除)Redis 中的旧数据。
  2. 线程1 更新(删除)数据库中的旧数据。

1.2 数据读取流程

数据读取流程分为两种情况:

  • Redis 中保存了数据,直接从 Redis 中查询。
  • Redis 中未保存数据,需要从数据库中查询,然后将这条数据保存到 Redis 中。

1.3 问题

在上述两个流程中,看似不发生冲突,实际上,在高并发的场景下会出现以下情况:

线程2 请求查询线程1 刚刚更新(删除)的数据,此时未在 Redis 中查询到对应的数据后,在数据库中进行查询,若此时线程1 未完成数据库中旧数据的更新(删除),此时线程2 读取从数据库中查询到旧的数据后,旧数据会被重新写入到 Redis 中。线程1 再完成更新(删除)数据库旧数据的操作,会导致 Redis 和数据库中的数据不一致。

1.4 解决方法

在线程1 完成删除数据库中旧数据的操作后,需要再对 Redis 执行一次更新(删除)操作。后续流程如下:

  1. 线程1 重新更新(删除)Redis 中的旧数据。

在高并发的场景下,这样操作虽然无法保证数据完全正确的读取也就是开头所说的强一致性,但是通过二次更新(删除)操作可以保证数据的最终一致性。

2. 先操作数据库

img

  1. 线程1 更新(删除)数据库中的数据。
  2. 线程1 更新(删除)Redis中的数据。

2.2 数据读取流程

数据读取流程也分为两种情况:

  • Redis 中保存了数据,直接从 Redis 中查询。
  • Redis 中未保存数据,需要从数据库中查询,然后将这条数据保存到 Redis 中。

2.3 问题

在上述数据更新流程中,正常情况下,能够保证数据的最终一致性。若在线程1 更新(删除)Redis中的数据失败了,此时后续的查询请求都会读到旧数据。

2.4 解决方法

在上述架构中加一层消息队列(MQ),若线程1 更新(删除)Redis中的数据失败,此时向消息队列中发送一条更新失败的请求,后端监听消息队列中更新失败的请求后重新执行更新(删除)Redis数据的操作。

缺点 :代码耦合度过高。