经过Gearman实现MySQL到Redis的数据复制

对于变化频率很是快的数据来讲,若是还选择传统的静态缓存方式(File System等)展现数据,可能在缓存的存取上会有很大的开销,并不能很好的知足须要,而Redis这样基于内存的NoSQL数据库,就很是适合担任实时数据的容器。php

可是每每咱们又有数据可靠性的需求,采用MySQL做为数据存储,不会由于内存问题而引发数据丢失,同时也能够利用关系数据库的特性实现不少功能。mysql

因此就会很天然的想到是否能够采用MySQL做为数据存储引擎,Redis则做为Cache。而这种需求目前尚未看到有特别成熟的解决方案或工具,所以本文将尝试采用Gearman+PHP+MySQL UDF的组合异步实现MySQL到Redis的数据复制。git

###MySQL到Redis数据复制方案 不管MySQL仍是Redis,自身都带有数据同步的机制,像比较经常使用的MySQL的Master/Slave模式,就是由Slave端分析Master的binlog来实现的,这样的数据复制其实仍是一个异步过程,只不过当服务器都在同一内网时,异步的延迟几乎能够忽略。github

那么理论上咱们也能够用一样方式,分析MySQL的binlog文件并将数据插入Redis。可是这须要对binlog文件以及MySQL有很是深刻的理解,同时因为binlog存在Statement/Row/Mixedlevel多种形式,分析binlog实现同步的工做量是很是大的。redis

所以这里选择了一种开发成本更加低廉的方式,借用已经比较成熟的MySQL UDF,将MySQL数据首先放入Gearman中,而后经过一个本身编写的PHP Gearman Worker,将数据同步到Redis。比分析binlog的方式增长了很多流程,可是实现成本更低,更容易操做。sql

###Gearman的安装与使用 Gearman是一个支持分布式的任务分发框架。设计简洁,得到了很是普遍的支持。一个典型的Gearman应用包括如下这些部分:数据库

###Gearman构架json

Gearman Job Server:Gearman核心程序,须要编译安装并以守护进程形式运行在后台 Gearman Client:能够理解为任务的收件员,好比我要在后台执行一个发送邮件的任务,能够在程序中调用一个Gearman Client并传入邮件的信息,而后就能够将执行结果当即展现给用户,而任务自己会慢慢在后台运行。 Gearman Worker:任务的真正执行者,通常须要本身编写具体逻辑并经过守护进程方式运行,Gearman Worker接收到Gearman Client传递的任务内容后,会按顺序处理。 之前曾经介绍过相似的后台任务处理项目Resque。二者的设计其实很是接近,简单能够类比为:ubuntu

Gearman Job Server:对应Resque的Redis部分 Gearman Client:对应Resque的Queue操做 Gearman Worker:对应Resque的Worker和Job 这里之因此选择Gearman而不是Resque是由于Gearman提供了比较好用的MySQL UDF,工做量更小。缓存

###安装Gearman及PHP Gearman扩展 如下均以Ubuntu12.04为例。

apt-get install gearman gearman-server libgearman-dev 检查Gearman的运行情况:

/etc/init.d/gearman-job-server status

  • gearmand is running 说明Gearman已经安装成功。

PHP的Gearman扩展能够经过pecl直接安装

pecl install gearman echo "extension=gearman.so" > /etc/php5/conf.d/gearman.ini service php5-fpm restart 可是实测发现ubuntu默认安装的gearman版本太低,直接运行pecl install gearman会报错

configure: error: libgearman version 1.1.0 or later required 所以Gearman + PHP扩展建议经过编译方式安装,这里为了简单说明,选择安装旧版本扩展:

pecl install gearman-1.0.3 ###Gearman + PHP实例 为了更容易理解后文Gearman的运行流程,这里不妨从一个最简单的Gearman实例来讲明,好比咱们要进行一个文件处理的操做,首先编写一个Gearman Client并命名为client.php:

<?php
$client = new GearmanClient();
$client->addServer();
$client->doBackground('writeLog', 'Log content');
echo '文件已经在后台操做';

运行这个文件,至关于模拟用户请求一个Web页面后,将处理结束的信息返回用户:

php client.php 查看一下Gearman的情况:

(echo status ; sleep 0.1) | netcat 127.0.0.1 4730

能够看到输出为

writeLog        1       0       0

说明咱们已经在Gearman中创建了一个名为writeLog的任务,而且有1个任务在队列等待中。

而上面的4列分别表明当前的Gearman的运行状态:

任务名称 在等待队列中的任务 正在运行的任务 正在运行的Worker进程 可使用watch进行实时监控:

watch -n 1 "(echo status; sleep 0.1) | nc 127.0.0.1 4730"

而后咱们须要编写一个Gearman Worker命名为worker.php:

<?php
$worker = new GearmanWorker();
$worker->addServer();
$worker->addFunction('writeLog', 'writeLog');
while($worker->work());

function writeLog($job)
{
        $log = $job->workload();
        file_put_contents(__DIR__ . '/gearman.log', $log . "\n", FILE_APPEND | LOCK_EX);
}

Worker使用一个while死循环实现守护进程,运行

php worker.php 能够看到Gearman状态变为:

writeLog        0       0       1

同时查看同目录下gearman.log,内容应为从Client传入的值Log content。

经过MySQL UDF + Trigger同步数据到Gearman MySQL要实现与外部程序互通的最好方式仍是经过MySQL UDF(MySQL user defined functions)来实现。为了让MySQL能将数据传入Gearman,这里使用了lib_mysqludf_json和gearman-mysql-udf的组合。

安装lib_mysqludf_json 使用lib_mysqludf_json的缘由是由于Gearman只接受字符串做为入口参数,能够经过lib_mysqludf_json将MySQL中的数据编码为JSON字符串

apt-get install libmysqlclient-dev
wget https://github.com/mysqludf/lib_mysqludf_json/archive/master.zip
unzip master.zip
cd lib_mysqludf_json-master/
rm lib_mysqludf_json.so
gcc $(mysql_config --cflags) -shared -fPIC -o lib_mysqludf_json.so lib_mysqludf_json.c

能够看到从新编译生成了 lib_mysqludf_json.so 文件,此时须要查看MySQL的插件安装路径:

mysql -u root -pPASSWORD --execute="show variables like '%plugin%';"
+---------------+------------------------+
| Variable_name | Value                  |
+---------------+------------------------+
| plugin_dir    | /usr/lib/mysql/plugin/ |
+---------------+------------------------+

而后将 lib_mysqludf_json.so 文件复制到对应位置:

cp lib_mysqludf_json.so /usr/lib/mysql/plugin/

最后登入MySQL运行语句注册UDF函数:

CREATE FUNCTION json_object RETURNS STRING SONAME 'lib_mysqludf_json.so';

安装gearman-mysql-udf 方法几乎同样:

apt-get install libgearman-dev
wget https://launchpad.net/gearman-mysql-udf/trunk/0.6/+download/gearman-mysql-udf-0.6.tar.gz
tar -xzf gearman-mysql-udf-0.6.tar.gz
cd gearman-mysql-udf-0.6
./configure --with-mysql=/usr/bin/mysql_config --libdir=/usr/lib/mysql/plugin/
make && make install

登入MySQL运行语句注册UDF函数:

CREATE FUNCTION gman_do_background RETURNS STRING SONAME 'libgearman_mysql_udf.so';
CREATE FUNCTION gman_servers_set RETURNS STRING SONAME 'libgearman_mysql_udf.so';

最后指定Gearman服务器的信息:

SELECT gman_servers_set('127.0.0.1:4730'); 经过MySQL触发器实现数据同步 最终同步哪些数据,同步的条件,仍是须要根据实际状况决定,好比我但愿将数据表data的数据在每次更新时同步,那么编写Trigger以下:

DELIMITER $$
CREATE TRIGGER datatoredis AFTER UPDATE ON data FOR EACH ROW BEGIN
SET @ret=gman_do_background('syncToRedis', json_object(NEW.id as `id`, NEW.volume as `volume`)); 
END$$
DELIMITER ;

尝试在数据库中更新一条数据查看Gearman是否生效。

Gearman PHP Worker将MySQL数据异步复制到Redis Redis做为时下当热的NoSQL缓存解决方案无需过多介绍,其安装及使用也很是简单:

apt-get install redis-server 
pecl install redis
echo "extension=redis.so" > /etc/php5/conf.d/redis.ini

而后编写一个Gearman Worker:redis_worker.php

#!/usr/bin/env php
<?
$worker = new GearmanWorker();
$worker->addServer();
$worker->addFunction('syncToRedis', 'syncToRedis');

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

while($worker->work());
function syncToRedis($job)
{
        global $redis;
        $workString = $job->workload();
        $work = json_decode($workString);
        if(!isset($work->id)){
                return false;
        }
        $redis->set($work->id, $workString);
}

最后须要将Worker在后台运行:

nohup php redis_worker.php &

经过这种方式将MySQL数据复制到Redis,经测试单Worker基本能够瞬时完成。

注意点 在实际操做中发现,Gearman UDF在每次MySQL服务重启后会丢失已经设置的服务器信息。由于时间有限没有深刻的调查缘由,而用了曲线救国的解决方法,让MySQL在每次服务启动时自动运行一次设置语句:

vi /var/lib/mysql/init_file.sql

加入

SELECT gman_servers_set('127.0.0.1:4730');

而后在/etc/mysql/my.cnf的[mysqld]小节下加入

init-file=/var/lib/mysql/init_file.sql

而后重启服务。

相关文章
相关标签/搜索