From  http://simongui.github.io/2016/12/02/improving-cache-consistency.html

A typically web application introduces an in-memory cache like memcache or redis to reduce load on the primary database for reads requesting hot data. The most primitive design looks something like Figure 1.

+--------------------------------+        +------------+        +----------------+
| database <--------+ web server +--------> cache |
| mssql, mysql, oracle, postgres | +------------+ | memcache/redis |
+--------------------------------+ +----------------+

Figure 1

Unfortunately this design is really common despite the many issues it introduces. I’ve seen some organizations with large scale applications still using this design and they maintain a bunch of hacks to overcome these issues which increases the systems operational complexity and sometimes surfaces as inconsistent data to end users.

Issue 1. Pool of connections to the cache services per web server instance

In a large application sometimes thousands of web server instances (especially in slower languages like Ruby) are hosting the web application. Each one has to maintain connections to the infrastructure the web application code communicates with directly. This can include primary databases like MSSQL, MySQL, Oracle, Postgres and cache services like Memcache or Redis. Each web server instance would for example have a pool of connections for each database or cache service instance it communicates with.

         --------------------------------------------------------------------------
| database (mssql, mysql, oracle, postgres) |
+----^--^-----------^--^-----------^--^-----------^--^-----------^--^----+
| | | | | | | | | |
N connections | | | | | | | | | |
| | | | | | | | | |
+------------+ +------------+ +------------+ +------------+ +------------+
| web server | | web server | | web server | | web server | | web server |
+------------+ +------------+ +------------+ +------------+ +------------+
| | | | | | | | | |
N connections | | | | | | | | | |
| | | | | | | | | |
-----v--v-----------v--v-----------v--v-----------v--v-----------v--v-----
| cache (memcache, redis) |
+------------------------------------------------------------------------+

Figure 2

This can be a strain on resources both on the web server but more importantly the database or cache service as shown in Figure 2. This is why I included a 16,384 connection benchmark in my benchmarks of Redis server libraries for Go to see how they scaled. It’s not uncommon to see 10,000or 20,000 connections to a Memcache or Redis server in a large system designed like this.

Issue 2. Many web app requests have to execute cache set operations

Similar to how a HTTP request may issue multiple SQL INSERT or UPDATE statements, multiple SET operations may be issued against the cache service. Even though these can be done asynchronously, they still consume resources on the web server and it would be great if the web servers only had to be concerned with updating the primary database.

Issue 3. No fault tolerance. Data loss if cache set operations fail

The typical sequence of operations of how Figure 2 in a web application would be designed would be as follows.

  • Update the primary database (MSSQL, MySQL, Oracle, Postgres, etc).
  • If the transaction fails return a HTTP error.
  • If the transaction succeeds send SET operations to the cache server(s) (memcache, redis, etc).

Any SET operation could fail even after retrying which puts the cache service(s) inconsistent with the primary database which could result in users seeing incorrect information. Even worse depending how the application is designed you could experience partial failures which results in users seeing partially correct and partially incorrect information after a change and a cache hit.

Some cache service protocols support sending multiple SET operations in one command but some do not. Not all web applications are smart enough to group SET operations that happen in different areas of the code into a single command either. If this is the case you could have partial failures where some of the SET operations succeeded and some failed.

Outside of retrying there’s not much the web application can do to eventually correct the missing cache SET operations. It has to retry and give up at some point. The cache will be serving cache hits that are inconsistent with the primary database until the cache key(s) invalidate via a TTL or some other process.

Messaging middleware

Sometimes this gets solved by messaging middleware like Kafka where the web applications push SET operations into Kafka and consumers pull changes from Kafka and execute the SET operations on the cache service(s). This greatly increases the cache consistency and allows the caches survive failures and catch up after short or long failures.

This introduces latency in the system. Changes may not be seen right away to users. Some web applications solve this by doing sticky sessions and caching in-memory in the web application to hide that data is inconsistent. Stale results are still possible if the web server fails and requests route to a different web server instance. This introduces complexity in the request routing tier of the system.

         +------------------------------------------------------------------------+
| database (mssql, mysql, oracle, postgres) |
+----^--^-----------^--^-----------^--^-----------^--^-----------^--^----+
| | | | | | | | | |
N connections | | | | | | | | | |
| | | | | | | | | |
+----+--+----+ +----+--+----+ +----+--+----+ +----+--+----+ +----+--+----+
| web server | | web server | | web server | | web server | | web server |
+----+--+----+ +----+--+----+ +----+--+----+ +----+--+----+ +----+--+----+
| | | | | | | | | |
N connections | | | | | | | | | |
| | | | | | | | | |
+----v--v-----------v--v-----------v--v-----------v--v-----------v--v----+
| message queue (kafka, rabbitmq) |
+----------------------------------^--^----------------------------------+
| |
N connections | |
| |
+------+--+------+
| kafka consumer |
+------+--+------+
| |
N connections | |
| |
+----------------------------------v--v----------------------------------+
| cache (memcache, redis) |
+------------------------------------------------------------------------+

Figure 3

As shown in Figure 3 this greatly reduces the connection load on the cache service but introduces a lot of operational complexity such as the following.

  • Deploy and operate a high throughput messaging system like Kafka with multiple brokers to survive broker failures.
  • Deploy and operate multiple consumer processes that consume messages in Kafka and execute SET operations to the cache service(s) to survive consumer failures.

Issue 4. No sequential consistency with the primary database

Leslie Lamport describes sequential consistency as follows.

The result of any execution is the same as if the operations of all the processors were executed in some sequential order, and the operations of each individual processor appear in this sequence in the order specified by its program.

Figure 3 greatly improves fault tolerance and reduces the chances of losing an update however does not address order. Issue 3 describes the possibility of complete and partial failures and explains how a user could see partially up-to-date and partially stale results. Diving deeper operations could fail before following operations succeed. The order of visible changes could be out-of-order. Some applications may be more sensitive to this kind of inconsistency. Some applications may require strict partial order. Even if order isn’t critical, providing sequential consistency is a better experience for users and less confusing.

Solution: MySQL binlog replication

Figure 3 shows the benefits of a shared message queue however deploying one with fault tolerance is not trivial and operating one smoothly isn’t trivial either. If you use a database with replication there’s already a queue in your system and you may not need to deploy yet another queue and new piece of infrastructure like Kafka to solve some of these problems.

+----------+---+---+---+---+---+   binlog replication   +--------------------------+
| MySQL | 1 | 2 | 3 | 4 | 5 <------------------------+ MySQL replication client |
+----------+---+---+---+---+---+ +--------------------------+
MySQL binlog
binlog positions

Figure 4

MySQL has a binlog replication protocol which is used for primary/secondary replication. This is essentially a replicated queue that has all the transactions recorded in-order as shown in Figure 4.

This isn’t a popular solution but I say, why not? It works very well. You can write an application that can speak the MySQL binlog replication protocol that consumes the binlog entries and execute SET operations against the cache service(s). There are two ways you could consume the binlog data.

  • Interpret the raw SQL syntax and issue SET operations.
  • The web application embeds cache keys as a comment in the SQL.

Both of these options are good because you can even get the transaction scope of each transaction in the binlog statements if you need to and if the target system supports atomic multi-set operations. I prefer the 2nd option because it’s easier to parse and the application already has this information in most cases.

         +------------+ +------------+ +------------+ +------------+ +------------+
| web server | | web server | | web server | | web server | | web server |
+------------+ +------------+ +------------+ +------------+ +------------+
| | | | | | | | | |
N connections | | | | | | | | | |
| | | | | | | | | |
+----v--v-----------v--v-----------v--v-----------v--v-----------v--v----+
| database (mssql, mysql,,oracle, postgres) |
+------------------------------------^-----------------------------------+
|
1 connection |
|
+---------------------------+
| binlog replication client |
+---------------------------+
| |
N connections | |
| |
+----------------------------------v--v----------------------------------+
| cache (memcache, redis) |
+------------------------------------------------------------------------+

Figure 5

Figure 5 shows the overall architecture with the binlog replication in place.

Benefits

  • Drastically reduces connection load on the cache service(s). Web servers only connect to the database.
  • Sequential consistency because we are reading the databases commit log into the cache service(s).
  • Possible to connect to any MySQL replica in the replication chain since they are all sequentially consistent.

I love Kafka and have nothing against it, I use it myself. Reducing infrastructure simplified the architecture and reduced operational complexity. By replicating the MySQL commit log to the cache service(s) we have increased the consistency as well as gained strict partial order between the database and the cache service(s).

I’m currently working on a project in Go that provides this proposed functionality that I’ll announce at a later date. Contact me if you want to know more about it.

Improving cache consistency redis和db的一致性维护的更多相关文章

  1. DB,Cache和Redis应用场景分析

    最近做一产品,微博方面的.数据存储同时用到了DB(mysql),Cache(memcache),Redis.其实最开始架构设计的时候是准备用MongoDB的,由于学习成本太高,最终选择放弃了,采用了比 ...

  2. Redis与DB的数据一致性解决方案(史上最全)

    文章很长,而且持续更新,建议收藏起来,慢慢读! 高并发 发烧友社群:疯狂创客圈(总入口) 奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : 极致经典 + 社群大片好评 < Java 高并发 三 ...

  3. SmartSql = Dapper + MyBatis + Cache(Memory | Redis) + ZooKeeper + R/W Splitting + ......

    SmartSql Why 拥抱 跨平台 DotNet Core,是时候了. 高性能.高生产力,超轻量级的ORM.156kb (Dapper:168kb) So SmartSql TargetFrame ...

  4. django自带cache结合redis创建永久缓存

    0916自我总结 django自带cache结合redis创建永久缓存 1.redis库 1.安装redis与可视化操作工具 1.安装redis https://www.runoob.com/redi ...

  5. Spring配置cache(concurrentHashMap,guava cache、redis实现)附源码

    在应用程序中,数据一般是存在数据库中(磁盘介质),对于某些被频繁访问的数据,如果每次都访问数据库,不仅涉及到网络io,还受到数据库查询的影响:而目前通常会将频繁使用,并且不经常改变的数据放入缓存中,从 ...

  6. springboot学习笔记-4 整合Druid数据源和使用@Cache简化redis配置

    一.整合Druid数据源 Druid是一个关系型数据库连接池,是阿里巴巴的一个开源项目,Druid在监控,可扩展性,稳定性和性能方面具有比较明显的优势.通过Druid提供的监控功能,可以实时观察数据库 ...

  7. 【Spring】17、spring cache 与redis缓存整合

    spring cache,基本能够满足一般应用对缓存的需求,但现实总是很复杂,当你的用户量上去或者性能跟不上,总需要进行扩展,这个时候你或许对其提供的内存缓存不满意了,因为其不支持高可用性,也不具备持 ...

  8. springboot整合spring @Cache和Redis

    转载请注明出处:https://www.cnblogs.com/wenjunwei/p/10779450.html spring基于注解的缓存 对于缓存声明,spring的缓存提供了一组java注解: ...

  9. Spring Boot(八)集成Spring Cache 和 Redis

    在Spring Boot中添加spring-boot-starter-data-redis依赖: <dependency> <groupId>org.springframewo ...

随机推荐

  1. SpagoBI系列----------[01]SpagoBI简介及安装步骤

    商务智能套件SpagoBI提供一个基于J2EE的框架用于管理BI对象如报表.OLAP分析.仪表盘.记分卡以及数据挖掘模型等的开源BI产品.它提供的BI管理器能 够控制.校验.验证与分发这些BI对象. ...

  2. ant如何编译项目

    Ant的概念 可能有些读者并不理解什么是Ant以及如何使用它,但只要使用通过Linux系统的读者,应该知道make这个命令.当编译Linux内核及一些软件的源程序时,经常要用这个命令.Make命令其实 ...

  3. target与currentTarget区别

    target在事件流的目标阶段:currentTarget在事件流的捕获,目标及冒泡阶段.只有当事件流处在目标阶段的时候,两个的指向才是一样的, 而当处于捕获和冒泡阶段的时候,target指向被单击的 ...

  4. java 实现一套流程管理、流转的思路(伪工作流)

    在做某个管理项目时,被要求实现一套流程管理,比如请假的申请审批流程等,在参考了很多资料,并和同事讨论后,得到了一个自主实现的流程管理. 以下提供我的设计思路,知道了思路,实现起来就简单很多了. 首先我 ...

  5. httpclient文件下载

    http://blog.csdn.net/nupt123456789/article/details/42721003

  6. Expression Blend实例中文教程(9) - 行为快速入门Behaviors

    在Blend强大的设计功能支持下,设计人员和开发人员可以无代码实现Silverlight/WPF动画效果,例如上文介绍的StoryBoard,就是一个典型例子,设计人员和开发人员仅需提供必要元素,即可 ...

  7. EV3DVue干涉检测的优势分析

    过去几年中国制造行业获得了的快速发展,各企业为了尽可能早的抢占市场,对模具的生产周期要求越来越短,精度要求越来越高,这就对模具设计以及制造等各个环节提出了更高的要求.随着CAD/CAM技术的深入应用, ...

  8. golang学习之win7下go web之revel安装

    接着上回记录的win7下go环境搭建,go的开发,现在除了sublime外,LiteIDE比较推荐,下载链接 下载安装后直接打开,需要配置下go环境(本机使用的是window 386版本),如下: 打 ...

  9. 2017年12月13日 LinQ用法基本的增删改查

    LinQ是什么? LinQ是语言集成的查询,是用于C#跟Vb的扩展语言 LinQ的用法 新建一个App_Code文件夹,在文件夹下添加一个数据LinQ to SQL类,可以直接直接点击服务器管理器然后 ...

  10. 一:Nginx知识整理

    Nginx与服务器集群 一:集群 1.什么是集群 集 合在一起 放在一起 群 一堆 很多 服务器架构集群:多台服务器组成的响应大并发,高数据量访问的架构体系. 分布式服务器架构: 特点: 1)成本高 ...