玉泉营网站建设,云南网站定制开发,网站权重对应的等级,泸州市建设规划局网站背景
使用Reids作为缓存的原因#xff1a; 在高并发场景下#xff0c;传统关系型数据库的并发能力相对比较薄弱#xff08;QPS不能太大#xff09;#xff1b; 使用Redis做一个缓存。让用户请求先打到Redis上而不是直接打到数据库上。 但是如果出现数据更新操作#xff…背景
使用Reids作为缓存的原因 在高并发场景下传统关系型数据库的并发能力相对比较薄弱QPS不能太大 使用Redis做一个缓存。让用户请求先打到Redis上而不是直接打到数据库上。 但是如果出现数据更新操作数据库与缓存更新就会出现缓存(Redis)和数据库(MySQL)之间的数据一致性问题。
非读写分离架构延时双删
先更新数据库再更新缓存为什么不可行
线程安全角度
同时有请求A、B进行更新操作
执行顺序如下 线程A更新了数据库 线程B更新了数据库 线程B更新了缓存 线程A更新了缓存
缓存和数据库出现了不一致
业务角度
如果某个业务场景是写多读少就会导致缓存并未被读取就会被频繁的更新极大的浪费了服务器的性能。 因为数据库的值并不是直接刷入缓存有的业务需要经过一系列复杂的计算再写入缓存。
先删除缓存再更新数据库为什么不可行
线程安全角度
请求A进行写操作先删除缓存 请求B查询发现缓存不存在 请求B去数据库查询得到旧值 请求B将旧值写入缓存 请求A更新数据库
此时数据库中的值是新值缓存的值是旧值就发生了数据不一致问题
延时双删
线程 A 当应用程序需要更新数据时首先将数据更新到数据库 A 线程向 Redis 发送删除缓存的指令将缓存标记为过期 A 线程等待一定的时间窗口(通常是几十ms几百ms)让 B 线程有足够的时间去访问缓存
线程 B 在时间窗口内当有请求访问过期的缓存数据时B 线程发现缓存已过期并触发缓存更新的操作 B 线程从数据库中获取到最新数据并将其存储到缓存中 B 线程返回更新后的缓存数据
线程 A续 在时间窗口结束后A 线程再次向 Redis 发送删除缓存的指令彻底删除缓存数据 如果在时间窗口内没有请求访问到过期的缓存数据A 线程会删除已标记为过期的缓存数据
通过上述流程 A 线程负责标记缓存过期并等待一段时间给 B 线程足够的时间去访问缓存并更新 B 线程则负责处理实际的缓存更新操作 这样即使在缓存更新期间有请求访问过期的缓存数据也能获取到最新的数据避免脏读
注意 具体的时间窗口大小和线程的实现方式可以根据实际需求和系统性能进行调整。 同时对于高并发环境还需要考虑线程安全和并发控制的实现以确保操作的正确性和性能。
读写分离架构有专门的读服务、专门的写服务写主读从主
可以采用先更新数据库再删除缓存配合上重试机制
问题
两个请求请求A进行更新操作请求B进行查询操作 请求A进行写操作删除缓存 请求A将数据写入数据库 请求B查询缓存发现缓存没有值 请求B去从库查询这时还没有完成主从同步因此查询到的是旧值 请求B将旧值写入缓存 数据库完成主从同步从库变为新值 仍然会出现缓存与数据库数据不一致问题
延时双删的问题
延时时间需要在主从同步的延时时间基础上加几百ms
双删失败 如果第二次删除缓存失败仍然会出现缓存与数据库数据不一致的问题
同样还是有两个请求请求A进行更新操作请求B进行查询操作单库 请求A进行写操作删除缓存 请求B查询发现缓存不存在 请求B去数据库查询得到旧值 请求B将旧值写入缓存 请求A将新值写入数据库 请求A试图去删除请求B写入的缓存值结果失败了
删除失败的重试补偿机制
先更新数据库再删除缓存 同样存在并发问题但是发生几率很低
两个请求请求A进行更新操作请求B进行查询操作单库 缓存刚好失效 请求A查询数据库得一个旧值 请求B将新值写入数据库 请求B删除缓存 请求A将查到的旧值写入缓存
该情况发生的必要条件就是请求B写数据库的操作比请求A读数据库的操作耗时更短才能使请求B先删除缓存 但是通常来说数据库的读操作是远远快于写操作的所以这种并发问题很难发生。
如果在极端情况下这种并发问题仍然发生了 给缓存设置一定的有效时间
异步延时双删策略
另起一个线程异步删除保证读请求完成以后再进行删除操作
重试机制 与先删除缓存再更新数据一样如果删除缓存失败那么仍然会出现数据不一致问题
选择靠谱的重试机制比如利用消息队列进行删除的补偿
方案一
更新数据库数据 缓存因为种种问题删除失败 将需要删除的key发送至消息队列 自己消费消息获得需要删除的key 继续重试删除操作直到成功 缺点 对业务线代码造成大量的侵入需要在业务代码中额外添加生成消息和消费消息的功能 业务代码变得不再专注于业务需求。
改进 启动一个订阅程序去订阅数据库的binlog获得需要操作的数据 在应用程序中另起一段程序避免业务侵入获得这个订阅程序传来的信息进行删除缓存操作
方案二
更新数据库数据 数据库会将操作信息写入binlog日志当中 订阅程序提取出所需要的数据以及key 另起一段非业务代码获得该信息 尝试删除缓存操作发现删除失败 将这些信息发送至消息队列 重新从消息队列中获得该数据重试操作订阅binlog程序在mysql中有现成的中间件叫canal 可以完成订阅binlog日志的功能
附录
MySQL的查询QPS主要取决于硬件性能以及应用的查询优化通常在千到万的范围 但在特定的配置下可以达到几万到十几万。
通常来说对于大多数应用场景而言MySQL的QPS在2000-3000就已经比较高了。
过高的QPS可能会对服务器性能产生负面影响如CPU和I/O压力过大。 因此最佳的QPS应根据实际的硬件配置和应用需求来定。
通过对查询的优化如合理的索引设计、合理的查询设计也能够有效提高QPS 通过使用缓存、读写分离、分库分表等方式也能显著提高系统的并发处理能力从而提高QPS
词汇
写入失败重试直到成功称之为删除补偿
学习文档
https://mp.weixin.qq.com/s?__bizMzg5MjE0MjE3Mwmid2247488095idx1snc4f50e3dbfd381c3f9c6948b973f8063chksmcfc3c56df8b44c7be1a7284a0f6b9255274fb50e3cefcf98e7e6897b1df0f8080223012a312b#rd