MySQL与Memcached整合

Published on 2016 - 06 - 09

要将Memcached较好地整合到系统架构中,首先要在应用系统中让Memcached有一个准确的定位:是仅仅作为提升数据服务性能的一个Cache工具,还是让它与MySQL数据库较好地融合在一起成为一个更为高效、理想的数据服务层。

作为提升系统性能的Cache工具

如果只是通过Memcached来提升系统性能,作为一个Cache软件,更多的是需要通过应用程序来维护Memcached中的数据与数据库中数据的同步更新。这时候的Memcached基本可以理解为比MySQL数据库更为前端的一个Cache层。

如果将Memcached作为应用系统的一个数据Cache服务,那么对于MySQL数据库来说基本上不用做任何改造,仅仅通过应用程序来对Cache进行维护更新。这样做最大的好处就在于可以完全不用调整数据库相关的架构,但是同时也会有一个弊端,那就是如果需要Cache的数据对象较多的时候,应用程序要增加的代码量就会很多,同时系统复杂度及维护成本也会直线上升。

下面是将Memcached用为简单的Cache服务层时的MySQL-Memcached架构示意图(图1)。

从图中可以看到,所有数据都会写入MySQL Master中,包括数据第一次写入时的INSERT,同时也包括对已有数据的UPDATE和DELETE。不过,如果是对已经存在的数据,则要在UPDATE或DELETE MySQL数据的同时,删除Memcached中的数据,以此保证整体数据的一致性。而所有的读请求首先会发往Memcached中,如果读取到数据则直接返回,没有读取到数据,则再到MySQL Slaves中读取数据,并将读取到的数据写入Memcached中进行Cache。

这种方式一般来说比较适用于缓存对象类型少,而缓存的数据量又比较大的环境,是一个快速有效的完全针对性能问题的解决方案。由于这种架构方式和MySQL数据库本身并没有太大关系,所以这里就不涉及太多的技术细节了。

和MySQL整合为数据服务层

除了将Memcached用作快速提升效率的工具之外,还可以将之利用到提高数据服务层的扩展性方面,和数据库整合成一个整体,或者作为数据库的一个缓冲。

首先看看如何将Memcached和MySQL数据库整合成一个整体来对外提供服务。一般来说,有两种方式可以做到。一种是直接利用Memcached的内存容量作为MySQL数据库的二级缓存,提升MySQL Server的缓存大小;另一种是通过MySQL的UDF来和Memcached进行数据通信,维护和更新Memcached中的数据,而应用端则直接通过Memcached来读取数据。

第一种方式主要用于业务要求非常特殊、实在难以进行数据切分,且很难通过对应用程序进行改造,利用数据库之外的Cache的场景。

当然,在正常情况下是肯定无法做到这一点的,至少目前必须借助外界的力量,开源项目Waffle Grid就是我们需要借助的外部力量。

Waffle Grid是国外的几位DBA在工作之余突发奇想而得到的一个点子:既然PC Server的低廉成本如此吸引我们,而其Scale Up的能力又很难有一个较大的突破,何不利用现在非常流行的Memcached作为突破单台PC Server的内存上限呢?就在这个想法的推动下,几位小伙子启动了Waffle Grid这个开源项目,利用MySQL和Memcached双双开源的特性,结合Memcached通信协议简单的特点,将Memcached成功实现成为MySQL主机的外部“二级缓存”,目前仅用于Innodb的Buffer Pool。

Waffle Grid的实现原理其实并不复杂,它所做的事情就是当Innodb在本地的Buffer Pool(我们称其为Local Buffer Pool)的时候,在从磁盘数据文件读取数据之前,先通过Memcached的通信API接口尝试从Memcached中读取相应的缓存数据(我们称之为Remote Buffer),只有在Remote Buffer中也不存在所需的数据时,Innodb才会访问磁盘文件读取数据。而且,只有处于Innodb Buffer Pool的 LRU List中的数据会被发送到Remote Buffer Pool中,而这些数据一旦被修改,Innodb就会将之移入FLUSH List,Waffle Grid同时会将进入FLUSH List的数据从Remote Buffer Pool中清除。所以可以说,Remote Buffer Pool中永远不会存在Dirty Pages,这也保证了当Remote Buffer Pool出现故障的时候不会出现数据丢失的问题。MySQL-Waffle Grid架构示意图(图2)比较清晰地展示出了使用Waffle Grid之后的数据库结构。

如架构图所示,首先在MySQL数据库端应用Waffle Grid Patch,通过它与其他的Memcached服务器通信。为了保证网络通信的性能,MySQL与Memcached之间尽可能用高带宽私有网络。

另外,架构图中并没有将数据库再区分成Master和Slave了(并不是说一定不能区分,只是示意图未这样做)。在实际应用过程中,大部分时候只需要在Slave上应用Waffle Grid即可,Master本身并不需要如此大的内存。

看了Waffle Grid的实现原理,可能某些读者会有疑问了:这样做不是所有需要产生物理读的Query的性能都会受到直接影响了吗?所有读取Remote Buffer的操作都须要通过网络来获取,其性能是否足够高呢?对此,我同样使用作者对Waffle的实测数据来解除大家的疑虑,如Waffle Grid性能测试结果图(图3)。

通过DBT2所得到的这组测试对比数据,在性能方面我想并不需要太多的担忧了吧。至于Waffle Grid是否适合您的应用场景,那就只能依靠各位读者自己评估了。

下面再来介绍一下Memcached和MySQL的另外一种整合方式,即通过MySQL所提供的UDF功能,自行编写相应的程序来实现MySQL与Memcached的数据通信更新操作。

这种方式和Waffle Grid不一样的是Memcached中的数据并不完全由MySQL来控制维护,而是由应用程序和MySQL一起来维护数据。每次应用程序从Memcached读取数据的时候,如果发现找不到自己需要的数据,则再转为从数据库中读取,然后将读取到的数据写入Memcached中。MySQL则控制Memcached中数据的失效清理工作,每次数据库中有数据被更新或删除的时候,MySQL则通过用户自行编写的UDF来调用Memcached的API通知Memcached某些数据已经失效,并删除该数据。

基于上面的实现原理,可以设计出如MySQL-Memcached架构二(图4)这样的数据服务层架构。

如图4所示,此架构与上面将Memcached完全和MySQL隔离开作为常规的Cache服务器比较,最大的区别在于Memcached的数据变为由MySQL数据库而不是应用程序来维护更新。首先数据被应用程序写入MySQL数据库,此时将触发MySQL上用户自行编写的相关UDF,然后通过该UDF调用Memcached的相关通信接口,将数据写入Memcached。而当MySQL中的数据被更新或删除的时候,UDF同样会更新或删除Memcached中的数据。当然,也可以让MySQL做更少一些的事情,仅仅只遇到数据被更新或删除的时候,通过UDF来删除Memcached中的数据,写入工作则像前面的架构一样由应用程序来完成。

由于Memcached基于对象的数据存取,以及通过Hash进行数据检索的特性,所以所有存储在Memcached中的数据都须要设定一个用于标识该数据的Key,所有数据的存取操作都通过该Key来进行。也就是说,并不能像MySQL的Query语句一样通过某一个(或者多个)关键字条件来读取包含多条数据的结果集,仅适用于通过某个唯一键来获取单条数据的数据读取方式。

参考文档