Redis和DB数据不一致解决方案

当并发访问时,Redis和数据库数据不一致成为常见问题。本文探讨两种优化方案:1) 双删加超时策略,在写库前后删除缓存并设置超时时间;2) 通过读取MySQL binlog异步淘汰缓存,降低业务代码侵入性,减少不一致时间。

大多情况下,我们使用缓存都是这样的策略:先读缓存,读取不到就读数据库然后同步到缓存中。

问题出现场景

问题就是在并发访问中,不论是先写库,再删除缓存;还是先删缓存,再写库,都有可能出现数据不一致的情况

1、在并发中是无法保证读写的先后顺序的,如果删掉了缓存还没来得及写库,另一个线程就过来读取发现缓存为空就去数据库读取并写入缓存,此时缓存中为脏数据。
2、如果先写了库,再删除缓存前,写库的线程宕机了,没有删除掉缓存,则也会出现数据不一致情况。
3、如果是redis集群,或者主从模式,写主读从,由于redis复制存在一定的时间延迟,也有可能导致数据不一致。

优化解决方案

双删 + 超时

在写库前后都进行redis.del(key)操作,并且设定合理的超时时间。这样最差的情况是在超时时间内存在不一致,当然这种情况极其少见,可能的原因就是服务宕机。此种情况可以满足绝大多数需求。
当然这种策略要考虑redis和数据库主从同步的耗时,所以在第二次删除前最好休眠一定时间,比如500毫秒,这样毫无疑问又增加了写请求的耗时

通过读取binlog的方式,异步淘汰缓存

这里写图片描述

好处:业务代码侵入性低,将缓存与数据库不一致的时间尽可能缩小。

binlog介绍:Mysql的binlog日志作用是用来记录mysql内部增删改查等对mysql数据库有更新的内容的记录(对数据库的改动),对数据库的查询select或

选择合适的RedisMySQL数据一致解决方案,需要综合考虑业务对数据一致性的要求、读写操作的频率、并发量以及系统的可用性等因素: ### 对数据一致性要求高、读多写少的场景 对于一些对数据一致性要求是特别严格,且读操作远远多于写操作的业务场景,如新闻资讯、商品列表展示等,可以采用更新数据库+删除缓存的策略。该策略在实现上相对简单,能在一定程度上保证数据的最终一致性,同时可以避免更新缓存带来的高成本问题。在更新成功后,即使面临并发读,也不会读到旧数据,因为缓存被删除后,后续读请求会从数据库中获取最新数据并更新缓存。过,若更新数据库失败,但缓存已被删除,可能会导致缓存穿透问题;在高并发场景下,还可能会出现短暂的数据一致情况[^1][^2]。 ```python import redis import mysql.connector # 连接Redis redis_client = redis.Redis(host='localhost', port=6379, db=0) # 连接MySQL mysql_connection = mysql.connector.connect( host="localhost", user="your_username", password="your_password", database="your_database" ) mysql_cursor = mysql_connection.cursor() def update_db_and_del_cache(): try: sql = "UPDATE your_table SET column1 = 'new_value' WHERE id = 1" mysql_cursor.execute(sql) mysql_connection.commit() redis_client.delete('your_cache_key') print("数据库更新成功,Redis缓存已删除") except Exception as e: print(f"操作失败: {e}") mysql_connection.rollback() # 调用函数 update_db_and_del_cache() # 关闭连接 mysql_cursor.close() mysql_connection.close() ``` ### 对数据一致性要求较高、读写较为均衡的场景 当业务对数据一致性有较高要求,且读写操作比较均衡时,可以考虑使用先更新数据库,后延迟删除缓存的策略。这种策略可以避免并发写并发读时缓存数据“脏读”的问题。通过延迟删除缓存,可以确保在更新数据库后,有足够的时间让其他可能的操作完成,减少数据一致的概率。过,需要处理好延迟期间的并发问题,并且引入消息队列会增加系统的复杂度。 ```python import redis import mysql.connector import time # 连接Redis redis_client = redis.Redis(host='localhost', port=6379, db=0) # 连接MySQL mysql_connection = mysql.connector.connect( host="localhost", user="your_username", password="your_password", database="your_database" ) mysql_cursor = mysql_connection.cursor() def update_db_then_del_cache(): try: sql = "UPDATE your_table SET column1 = 'new_value' WHERE id = 1" mysql_cursor.execute(sql) mysql_connection.commit() # 延迟删除缓存 time.sleep(0.01) redis_client.delete('your_cache_key') print("数据库更新成功,Redis缓存已延迟删除") except Exception as e: print(f"操作失败: {e}") mysql_connection.rollback() # 调用函数 update_db_then_del_cache() # 关闭连接 mysql_cursor.close() mysql_connection.close() ``` ### 对数据一致性要求极高的场景 对于金融交易、订单处理等对数据一致性要求极高的业务场景,需要采用强一致性策略。这种策略能确保任何时刻,缓存中的数据数据库中的数据都保持完全一致。然而,实现强一致性需要引入复杂的分布式事务管理机制,会极大地降低系统的性能可用性。因为要保证数据的强一致性,往往需要对数据缓存进行加锁操作,这会导致系统的并发处理能力下降,响应时间变长,并且在分布式环境中,处理网络延迟、节点故障等问题也会更加复杂[^1]。 ### 高并发写场景 在高并发写的业务场景中,先删除缓存,后更新数据库的策略可能更合适。该策略适用于可以容忍最终一致性的系统。更新成功后,即使有并发读操作,也不会读到旧数据,因为缓存已经被删除,后续读请求会从数据库获取最新数据并更新缓存。但当更新数据库失败时,缓存已经被删除,可能会导致大量请求直接访问数据库,造成缓存穿透问题;并且在删除缓存后、更新数据库前,如果有读请求进入,会读取到旧数据并更新到缓存中,导致数据一致[^2]。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值