php resque gearman,通过Gearman实现MySQL到Redis的数据复制

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

但是往往我们又有数据可靠性的需求,采用MySQL作为数据存储,不会因为内存问题而引起数据丢失,同时也可以利用关系数据库的特性实现很多功能。

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

MySQL到Redis数据复制方案

无论MySQL还是Redis,自身都带有数据同步的机制,像比较常用的MySQL的Master/Slave模式,就是由Slave端分析Master的binlog来实现的,这样的数据复制其实还是一个异步过程,只不过当服务器都在同一内网时,异步的延迟几乎可以忽略。

那么理论上我们也可以用同样方式,分析MySQL的binlog文件并将数据插入Redis。但是这需要对binlog文件以及MySQL有非常深入的理解,同时由于binlog存在Statement/Row/Mixedlevel多种形式,分析binlog实现同步的工作量是非常大的。

因此这里选择了一种开发成本更加低廉的方式,借用已经比较成熟的MySQL UDF,将MySQL数据首先放入Gearman中,然后通过一个自己编写的PHP Gearman Worker,将数据同步到Redis。比分析binlog的方式增加了不少流程,但是实现成本更低,更容易操作。

Gearman的安装与使用

Gearman是一个支持分布式的任务分发框架。设计简洁,获得了非常广泛的支持。一个典型的Gearman应用包括以下这些部分:

gearman_stack.pngGearman Job Server:Gearman核心程序,需要编译安装并以守护进程形式运行在后台

Gearman Client:可以理解为任务的收件员,比如我要在后台执行一个发送邮件的任务,可以在程序中调用一个Gearman Client并传入邮件的信息,然后就可以将执行结果立即展示给用户,而任务本身会慢慢在后台运行。

Gearman Worker:任务的真正执行者,一般需要自己编写具体逻辑并通过守护进程方式运行,Gearman Worker接收到Gearman Client传递的任务内容后,会按顺序处理。

以前曾经介绍过类似的后台任务处理项目Resque。两者的设计其实非常接近,简单可以类比为: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已经安装成功。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个任务在队列等待中。任务名称

在等待队列中的任务

正在运行的任务

正在运行的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

安装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

然后重启服务。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值