通过Gearman实现MySQL到Redis的数据同步
对于变化频率非常快的数据来说,如果还选择传统的静态缓存方式(Memocached、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 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已经安装成功。
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.0or later required
因此Gearman + PHP扩展建议通过编译方式安装,这里为了简单说明,选择安装旧版本扩展:
pecl install gearman-1.0.3
Gearman + PHP实例
为了更容易理解后文Gearman的运行流程,这里不妨从一个最简单的Gearman实例来说明,比如我们要进行一个文件处理的操作,首先编写一个Gearman Client并命名为client.php:
<?php
$client =newGearmanClient();
$client->addServer();
$client->doBackground('writeLog','Log content');
echo '文件已经在后台操作';
运行这个文件,相当于模拟用户请求一个Web页面后,将处理结束的信息返回用户:
php client.php
查看一下Gearman的状况:
(echo status ; sleep 0.1)| netcat127.0.0.14730
可以看到输出为
writeLog 100.
说明我们已经在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 =newGearmanWorker();
$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 001
同时查看同目录下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 =newGearmanWorker();
$worker->addServer();
$worker->addFunction('syncToRedis','syncToRedis'); $redis =newRedis();
$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)){returnfalse;}
$redis->set($work->id, $workString);}
最后需要将Worker在后台运行:
nohup php redis_worker.php &
通过这种方式将MySQL数据复制到Redis,经测试单Worker基本可以瞬时完成。
Ubuntu 14.04下Redis安装及简单测试 http://www.linuxidc.com/Linux/2014-05/101544.htm
Redis集群明细文档 http://www.linuxidc.com/Linux/2013-09/90118.htm
Ubuntu 12.10下安装Redis(图文详解)+ Jedis连接Redis http://www.linuxidc.com/Linux/2013-06/85816.htm
Redis系列-安装部署维护篇 http://www.linuxidc.com/Linux/2012-12/75627.htm
CentOS 6.3安装Redis http://www.linuxidc.com/Linux/2012-12/75314.htm
Redis安装部署学习笔记 http://www.linuxidc.com/Linux/2014-07/104306.htm
Redis配置文件redis.conf 详解 http://www.linuxidc.com/Linux/2013-11/92524.htm
Redis 的详细介绍:请点这里
Redis 的下载地址:请点这里
from:http://avnpc.com/pages/mysql-replication-to-redis-by-gearman%20
本文永久更新链接地址:http://www.linuxidc.com/Linux/2015-01/111380.htm
通过Gearman实现MySQL到Redis的数据同步的更多相关文章
- JAVA通过Gearman实现MySQL到Redis的数据同步(异步复制)
MySQL到Redis数据复制方案 无论MySQL还是Redis,自身都带有数据同步的机制,像比较常用的 MySQL的Master/Slave模式 ,就是由Slave端分析Master的binlog来 ...
- Gearman + Nodejs + MySQL UDF异步实现 MySQL 到 Redis 的数据同步
[TOC] 1, 环境 CentOS, MySQL, Redis, Nodejs 2, Redis简介 Redis是一个开源的K-V内存数据库,它的key可以是string/set/hash/list ...
- TiDB 作为 MySQL Slave 实现实时数据同步
由于 TiDB 本身兼容绝大多数的 MySQL 语法,所以对于绝大多数业务来说,最安全的切换数据库方式就是将 TiDB 作为现有数据库的从库接在主 MySQL 库的后方,这样对业务方实现完全没有侵入性 ...
- Redis进行数据同步
数据库中的数据一般都涉及到需要对数据进行备份的,这样可以保证数据的安全性,并且如果将一个主设备的数据同步到多个从设备上,允许用户访问数据时可以从多个从设备进行读取, 这样还可以缓解主设备的压力,Red ...
- 从MySQL到Redis 提升数据迁移的效率
场景是从MySQL中将数据导入到Redis的Hash结构中.当然,最直接的做法就是遍历MySQL数据,一条一条写入到Redis中.这样可能没什么错,但是速度会非常慢.而如果能够使MySQL的查询输出数 ...
- Mysql主从复制,实现数据同步
大型网站为了软解大量的并发访问,除了在网站实现分布式负载均衡,远远不够.到了数据业务层.数据访问层,如果还是传统的数据结构,或者只是单单靠一台服务器扛,如此多的数据库连接操作,数据库必然会崩溃,数据丢 ...
- 基于 MySQL Binlog 的 Elasticsearch 数据同步实践 原
一.背景 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品.订单等数据的多维度检索. 使用 Elasticsearch 存储业务数据可以 ...
- 基于MySQL Binlog的Elasticsearch数据同步实践
一.为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品.订单等数据的多维度检索. 使用 Elasticsearch 存储业务数 ...
- mysql向redis导入数据
数据库结构如下 如果是linux系统下,如此整备数据 SELECT CONCAT( "*10\r\n", '$', LENGTH(redis_cmd), '\r\n',redis_ ...
随机推荐
- gcc编译参数-fPIC的一些问题
gcc编译参数-fPIC的一些问题 (2012-07-26 15:41:08) 转载▼ 标签: linux compiler gcc -fpic it 分类: NSN_BspDriver ppc_85 ...
- plain framework 1 参考手册 入门指引之 简明教程
简明教程 简单的例子 实现代码 简单的例子 如果你已经下载好整个框架的源码,那么你可以在这里找到应用的例子: plainframework/applications/pf_simple 如果你在win ...
- 连载《一个程序猿的生命周期》-28、被忽悠来的单身HR(女同志)
一个程序猿的生命周期 微信平台 口 号:职业交流,职业规划:面对现实,用心去交流.感悟. 公众号:iterlifetime 百木-ITer职业交流奋斗 群:141588103 微 博:h ...
- Hacker communities collection
Copy from E安全 Hack Forums: Hack Forums是目前最为理想的黑客技术学习根据地.该论坛不仅在设计上面向黑客群体,同时也适用于开发人员.博主.游戏开发者.程序员.图形设计 ...
- POJ 1696 Space Ant --枚举,模拟,贪心,几何
题意: 有很多点,从最右下角的点开始走起,初始方向水平向右,然后以后每步只能向左边走,问最多能走多少个点. 解法: 贪心的搞的话,肯定每次选左边的与它夹角最小的点,然后走过去. 然后就是相当于模拟地去 ...
- [No000003]现代版三十六计,计计教你如何做人
<现代版三十六计,计计教你如何做人> …………………………………………………………………………………… 第1计施恩计 在人际交往中,见到给人帮忙的机会,要立马扑上去,像一只饥饿的松鼠扑向地 ...
- python日期格式化与绘图
画一个量随着时间变化的曲线是经常会遇到的需求,比如画软件用户数的变化曲线.画随时间变化的曲线主要用到的函数是matplotlib.pyplot.plot_date(date,num).由于其第一个变量 ...
- 第七课——iOS数据持久化
今天我们要学习plist.Preference.NSKeyedArchiver.Sqlite.CoreData(属性列表.偏好设置.归档.数据库.模型化对象). 首先plist就是将某些特定类,通过X ...
- JavaScript Math 对象
JavaScript Math 对象 Math 对象 Math 对象用于执行数学任务. Math 对象并不像 Date 和 String 那样是对象的类,因此没有构造函数 Math(). 语法 var ...
- li标签包含img的问题
我们在制作页面时,经常有可能碰到这样的设计: li 图一 图一的布局代码如下: <ul> <li><img src=”pic1.jpg” />& ...