利用gearman實現redis緩存mysql

环境:

centos6.5

mysql5.6

gearman简介:

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

    • Gearman Job Server:Gearman核心程序,以守护进程形式运行在后台

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

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

设计思路:

首先利用mysql UDF(通过了lib_mysqludf_json和gearman-mysql-udf的组合实现)在mysql中的数据发生改变时触动触发器将数据传入Gearman中,这时的mysql相当于Gearman的clinet。然后运行自己编写的php程序作为worker,将Gearman中的数据传到Redis中去,这时的Redis相当于是Gearman的consumer。

1、安装gearman

实验中的系统yum源在centos6.5自带的网络yum源的基础上,增加了epel的源,EPEL (Extra Packages for Enterprise Linux,企业版Linux的额外软件包) 是Fedora小组维护的一个软件仓库项目,为RHEL/CentOS提供他们默认不提供的软件包。使用这个源可以免去很多麻烦,省去源码编译的麻烦,需要注意的是,不论是使用centos自带的网络yum源还是epel扩展源,都需要你的IP能够访问到公网。

安装gearman、php、php的gearman扩展、nc工具

yum install -y php-pecl-gearman libgearman libgearman-devel gearmand nc

启动gearman服务

/etc/init.d/gearmand start

验证gearman是否成功启动,如果返回的结果中有4730端口,那么表示服务已经正常启动了

[root@hadoop1 ~]# netstat -alnutp |grep gearman

2、模拟Gearman的工作原理:

使用下列命令查看Gearman的队列

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

结果如下

writeLog 0 0 0

四列含义:1-任务名称;2-等待队列任务数;3-运行中的任务数;4-正在运行的worker进程数;

编译一段php代码模拟Gearman的Client:client.php

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

执行client.php

php client.php

这时,再次查看Gearman的队列,发现等待队列中有一个任务

writeLog 1 0 0

编写一段php代码模拟Gearman的Worker:worker.php

该worker的作用是将客户端传递给Gearman的字符串'Log content'写入到当前目录下的gearman.log文件中

<?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);
}

以nohup的方式后台启动worker.php

nohup php worker.php &

再次查看Gearman的队列,发现等待的任务变成0,worker进程变成了1,gearman.log有了内容

writeLog 0 0 1

[root@hadoop1 ~]# cat gearman.log
Log content

3、安装mysql-server、mysql、php-mysql(php连接mysql的驱动,非必须,这里是为了稍后用程序比较从Redis和Mysql中分别读取数据的效率)。实验中,由于我的机子上之前已将安装了mysql5.6,所以就直接使用mysql5.6做实验了。当然也可以使用yum开安装mysql,可能安装的mysql版本不是5.6,但是完全没有关系。

安装mysql相关软件

yum install -y mysql-server mysql php-mysql

启动mysql

/etc/init.d/mysql start

4、安装lib_mysqludf_json

wget https://github.com/mysqludf/lib_mysqludf_json/archive/master.zip
mv master master.zip
unzip master.zip
cd lib_mysqludf_json-master
rm -rf lib_mysqludf_json.so
gcc $(mysql_config --cflags) -shared -fPIC -o lib_mysqludf_json.so lib_mysqludf_json.c

这时重新编译生成了lib_mysqludf_json.so,然后需要把lib_mysqludf_json.so拷贝到mysql的插件目录下,查看mysql的插件目录:

[root@hadoop1 ~]# mysql -u root -pupbjsxt --execute="show variables like '%plugin%';"
+---------------+--------------------------+
| Variable_name | Value |
+---------------+--------------------------+
| plugin_dir | /usr/lib64/mysql/plugin/ |
+---------------+--------------------------+

[root@hadoop1 lib_mysqludf_json-master]# cp lib_mysqludf_json.so /usr/lib64/mysql/plugin/

5、安装gearman-mysql-udf

wget https://launchpad.net/gearman-mysql-udf/trunk/0.6/+download/gearman-mysql-udf-0.6.tar.gz
tar xf gearman-mysql-udf-0.6.tar.gz -C ./
cd gearman-mysql-udf-0.6
./configure --with-mysql=/usr/bin/mysql_config --libdir=/usr/lib64/mysql/plugin/
make && make install

该插件直接安装到了mysql的插件目录下。

6、连入mysql,创建对应的function、trigger及设置gearman server信息

[root@hadoop1 ~]# mysql -u root -p******
mysql> CREATE FUNCTION json_object RETURNS STRING SONAME 'lib_mysqludf_json.so';
mysql> CREATE FUNCTION gman_do_background RETURNS STRING SONAME 'libgearman_mysql_udf.so';
mysql> CREATE FUNCTION gman_servers_set RETURNS STRING SONAME 'libgearman_mysql_udf.so'

## 为javashop中的test表建立触发器
mysql> use javashop;
mysql> describe test;
+-------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+----------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | char(20) | YES | | NULL | |
+-------+----------+------+-----+---------+-------+
2 rows in set (0.00 sec)
mysql> source trigger.sql

## 设置gearman server信息
mysql>SELECT gman_servers_set('127.0.0.1:4730');

trigger.sql脚本文件内容如下

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

7、在mysql中更新一条数据,然后查看gearman的队列

mysql> update test set name='redis' where id=10;
Query OK, 1 row affected (0.03 sec)
Rows matched: 1 Changed: 1 Warnings: 0

writeLog 0 0 1
syncToRedis 1 0 0

可见mysql触发器已经成功的将数据传入到gearman中了。

8、安装redis、php-pecl-redis(php连接redis的驱动)

yum install php-pecl-redis redis -y

启动redis

/etc/init.d/redis start

登录redis客户端,查看当前内存中的数据

[root@hadoop1 ~]# redis-cli
redis 127.0.0.1:6379> keys *
(empty list or set)

这时,redis中并没有数据。

9、编写一个worker程序,负责将gearman中的数据传入到redis中去:redis_worker.php

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

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

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);
}

10、以nohup的方式后台运行redis_worker.php

nohup php redis_worker.php &

这时,再查看gearman的队列

writeLog 0 0 1
syncToRedis 0 0 1

发现syncToRedis任务之前等待的任务数变为了0,正在运行的worker进程数变为了1。

检查redis中是否缓存了数据

[root@hadoop1 ~]# redis-cli
redis 127.0.0.1:6379> keys *
1) "10"
redis 127.0.0.1:6379> get 10
"{\"id\":10,\"name\":\"redis\"}"

发现redis中已经成功的缓存了mysql中更新的数据,至此功能实现。

11、从redis中读取数据和从mysql中读取数据性能比较

编写php代码,从redis中读取数据:php_redis.php

<?php
$stime=microtime(true); //获取程序开始执行的时间

$redis = new Redis();
$redis->connect('127.0.0.1');
echo $redis->get('10');
echo "\n";
$redis->close();

$etime=microtime(true);//获取程序执行结束的时间

$total=$etime-$stime; //计算差值
echo "$total".'秒';
echo "\n";
?>

编写php代码,从mysql中读取数据:php_mysql.php

<?php
$stime=microtime(true); //获取程序开始执行的时间
$con = mysql_connect("127.0.0.1","root","******");
$r2 = mysql_select_db("javashop");

$result = mysql_query("SELECT * FROM test limit 1");
while ($row = mysql_fetch_array($result)) {
echo $row['id'] . " --> " . $row['name'];
echo "\n";
}
mysql_close($con);

$etime=microtime(true);//获取程序执行结束的时间

$total=$etime-$stime; //计算差值
echo "$total".'秒';
echo "\n"
?>

分别运行两个php程序

[root@hadoop1 ~]# php php_redis.php
{"id":10,"name":"redis"}
0.00059199333190918秒

[root@hadoop1 ~]# php php_mysql.php
10 --> redis
0.0043718814849854秒

[root@hadoop1 ~]# bc <<< 0.0043718814849854/0.00059199333190918
7

通过对一条记录的查询,可以发现,从mysql中获取数据的时长是redis中获取数据时长的7倍,可见性能的提升几乎达一个数量级。

注意点:

亲测印证了mysql在重启后会丢失之前设置的gearman server的信息,解决辦法如下:

在mysql的datadir目录下创建init_file.sql文件,内容为gearman server的信息的设置

echo "SELECT gman_servers_set('127.0.0.1:4730');" > /var/lib/mysql/init_file.sql

然后在mysql的配置文件的[mysqld]项中添加如下内容

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

然后重启mysql,更新一条记录再试试看!

参考:http://avnpc.com/pages/mysql-replication-to-redis-by-gearman

PS:完成了第一篇博客,希望大家多多指教,祝生活愉快!

本文出自 “勇敢向前,坚决向左” 博客,转载请与作者联系!

更多相关文章
  • 使用AOP 實現Redis緩存注解,支持SPEL
    公司项目对Redis使用比较多,因为之前没有做AOP,所以缓存逻辑和业务逻辑交织在一起,维护比较艰难所以最近实现了针对于Redis的@Cacheable,把缓存的对象依照类别分别存放到redis的Hash中,对于key也实现了SPEL支持. 1.applicationContext.xml,配置Je ...
  •      本文所指TwoQueues缓存模型,是说数据在内存中的缓存模型.      无论何种语言,都可能需要把一部分数据放在内存中,避免重复运算.读取.最常见的场景就是JQuery选择器,有些Dom元素的选取是非常耗时的,我们希望能把这些数据缓存起来,不必每次调用都去重新遍历Dom树.       ...
  • 


    		    Saltstack 利用pillar實現redis多實例部署
    需求:基于业务环境,需在一台机器上部署N多redis实例,之前部署只能实现部署单个redi
  • 


    		    Nginx之LNMP、LNNMP、LNNNMP架構實現及緩存技術
    1. 前言 1.1 Nginx简介 Nginx是一款由俄罗斯程序员Igor Sysoev所
  • 通过spring.net中的spring.caching CacheResult实现memcached缓存1.SpringMemcachedCache.cs2.APP.config3.Program.cs4.Common 待解决问题:CacheResult,CacheResultItems有什么区别
  • 使用ASIHTTPRequest和ASIDownloadCache實現本地緩存
      源码:http://files.cnblogs.com/ios8/ASIHttpReq
  • 很多论坛.评论都有过滤关键字的功能.但是很多系统,都使用的是一对一的过滤形式. 比如要过滤"色情",那么就是考虑内容里边是否包含"色情"这个词.这种过滤方式对于使用特殊字符连接的敏感字就没辦法了.比如"色.情",中间加一个点,就没辦法识别. ...
  • 本文只适合java菜鸟看,大大请多指教. 关于AOP: 百度百科:面向切面编程(也叫面向方面编程):Aspect Oriented Programming(AOP),是软件开发中的一个热点,也是Spring框架中的一个重要内容.利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的 ...
一周排行
  • 上一篇文章中,我们主要介绍了,Trip数据库的检索功能! 这一篇文章中,我们简单介绍一下,Trip数据库的增,删,改等东西. 我把demo放在一个tripdemo2文件夹中了!需要的话您去下载一下就OK了. 希望对大 ...
  • Majority Element
    1 class Solution { 2 public: 3 int majorityEl
  • 改造了一下maxdepth的代码 public static int minDepth(TreeNode root) { if(root==null) return 0; int height=0; Queue< ...
  • 轉奇異值分解(We Recommend a Singular Value Decomposition)
    文章转自:奇异值分解(We Recommend a Singular Value Deco
  • <?php ****************************************PHP高级程序员必修课************************************************ ...
  • 前两天需要将我们的一个新系统重新部署到另一服务器上,将数据库新建好后,从原来的数据库中导出数据,在导入到新的数据库中,启动服务的时候发现服务起不起来检查发现是数据库中一些表没有导入,经过查询后发现没有导入的表都是空的
  • CComboBox(组合框)控件 CComboBox类常用成员 CComboBox插入数据 CComboBox删除数据 CComboBox运用示例   一.CComboBox控件常用属性    Disabled   
  • 新買一款印表機hp5525N
    11900 RMB  彩色.激光.彩打. 让法国的工艺工程师给改成法语的了.
  •   Attributes     元素的属性可以为你的应用程序包含有用的信息,重要的是能够获取和设置它.   .attr()方法     .attr()方法是可获取和可设置的,在设置状态下,.attr()可以接收一个
  • 背景 会有朋友问我为啥用命令模式(Command Pattern)组织应用层,先看看MartinFowler咋说:http://martinfowler.com/bliki/CommandOrientedInterf