一、redis简介
Redis是一个key-value存储系统。和Memcached类似,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更 新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。在部分场合可以对关系数据库起到很好的 补充作用。它提供了Java,C/C++(hiredis),C#,PHP,JavaScript,Perl,Object-C,Python,Ruby等客户端,使用很方便。
二、架构图
![3.png wKioL1UaFsii5Z8qAADOf7iND80904.jpg](http://static.oschina.net/uploads/img/201602/04085103_sXMu.jpg)
大致结构就是读写分离,将mysql中的数据通过触发器同步到redis中
三、安装LNMP环境(这里为了省事,就是用yum来安装)
1、修改yum源
01 | [root@redis ~] # vim /etc/yum.repos.d/epel.repo #添加这个文件 |
03 | name=Extra Packages for Enterprise Linux 6 - $basearch |
04 | baseurl=http: //download .fedoraproject.org /pub/epel/6/ $basearch |
05 | failovermethod=priority |
11 | baseurl=http: //nginx .org /packages/centos/6/ $basearch/ |
2、yum安装
1 | [root@redis ~] # yum -y install nginx php php-fpm php-cli php-common php-gd php-mbstring php-mysql php-pdo php-devel php-xmlrpc php-xml php-bcmath php-dba php-enchant mysql mysql-server |
3、简单配置一下nginx
01 | [root@redis ~] # vim /etc/nginx/nginx.conf |
05 | server_name www.xx.com; |
08 | access_log /logs/www .xx.com.access.log main; |
12 | root /www/ ; #定义服务器的默认网站根目录位置 |
13 | index index.php index.html index.htm; #定义首页索引文件的名称 |
17 | fastcgi_pass 127.0.0.1:9000; |
18 | fastcgi_index index.php; |
19 | fastcgi_param SCRIPT_FILENAME /www/ $fastcgi_script_name; |
20 | include fastcgi_params; |
4、启动服务
01 | [root@redis ~] # sed -i 's/apache/nginx/g' /etc/php-fpm.d/www.conf |
02 | [root@redis ~] # /etc/init.d/php-fpm start |
05 | [root@redis ~] # /etc/init.d/mysqld start |
08 | [root@redis ~] # mkdir /{logs,www} |
09 | [root@redis ~] # chown -R nginx:nginx /{logs,www} |
10 | [root@redis ~] # /etc/init.d/nginx start |
13 | [root@redis www] # service iptables stop |
14 | iptables: Flushing firewall rules: [ OK ] |
15 | iptables: Setting chains to policy ACCEPT: filter [ OK ] |
16 | iptables: Unloading modules: [ OK ] |
18 | [root@redis redis] # netstat -tnlp #查看监听 |
19 | Active Internet connections (only servers) |
20 | Proto Recv-Q Send-Q Local Address Foreign Address State PID /Program name |
21 | tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 2101 /nginx |
22 | tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN 7544 /php-fpm |
23 | tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN 1871 /mysqld |
5、给mysql授权
2 | mysql> grant all privileges on *.* to root@localhost identified by '123456' ; |
4 | mysql> flush privileges; |
6、测试
1 | [root@redis ~] # vim /www/index.php |
然后访问页面看到php的相关信息,基础环境就算搭建完成了。
四、安装redis
1、安装redis
01 | [root@redis ~] # wget -c -t 0 http://download.redis.io/releases/redis-2.8.19.tar.gz |
03 | [root@redis ~] # mkdir /usr/local/redis |
05 | [root@redis ~] # tar xvf redis-2.8.19.tar.gz |
07 | [root@redis ~] # cd redis-2.8.19 |
08 | [root@redis redis-2.8.19] # make |
10 | #编译完成后,将src中的可执行文件拷贝到刚刚创建的目录中 |
11 | [root@redis src] # cp redis-benchmark redis-check-aof redis-check-dump redis-cli redis-sentinel redis-server /usr/local/redis/ |
12 | [root@redis redis-2.8.19] # cp redis.conf sentinel.conf /usr/local/redis/ |
Redis-benchmark 压力测试工具
Redis-check-aof 检查redis持久化命令文件的完整性
Redis-check-dump 检查redis持久化数据文件的完整性
Redis-cli redis在linux上的客户端
Redis-sentinel redis-sentinel是集群管理工具,主要负责主从切换。
Redis-server Redis服务器的daemon启动程序
2、安装php的redis扩展
01 | [root@redis ~] # wget -c -t 0 https://github.com/owlient/phpredis/archive/master.zip |
02 | [root@redis ~] # unzip master.zip |
04 | [root@redis ~] # cd phpredis-master/ |
05 | [root@redis phpredis-master] # phpize |
06 | [root@redis phpredis-master] # ./configure --with-php-config=/usr/bin/php-config |
07 | [root@redis phpredis-master] # make && make install |
09 | #修改php的配置文件,如果没有“extension=redis.so”,就加上这一行 |
10 | [root@redis ~] # vim /etc/php.ini |
13 | [root@redis ~] # /etc/init.d/php-fpm restart |
3、是否安装成功
还是访问phpinfo的那个界面
![2.png wKioL1UaEDeBJuP3AABbp2hjrt4347.jpg](http://static.oschina.net/uploads/img/201602/04085103_uHbo.jpg)
看到这个就是安装完成了。
五、读写分离
这里只是简单的做了一下读,没有写操作的相关代码,过一会测试,直接到数据库里执行update来模拟写操作。
1、在mysql中插入一些测试数据
01 | [root@redis ~] # mysql -u root -p123456 |
02 | mysql> create database mytest; |
03 | mysql> CREATE TABLE ` test ` (` id ` int(7) NOT NULL AUTO_INCREMENT, `name` char(8) DEFAULT NULL, PRIMARY KEY (` id `)) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8; |
04 | mysql> INSERT INTO ` test ` VALUES (1, 'sven' ),(2, 'jim' ),(3, 'zhu' ),(4, 'wang' ),(5, 'ftd' ),(6, 'test' ),(7, 'test01' ),(8, 'test02' ),(9, 'test03' ); |
05 | mysql> select * from mytest. test ; |
2、编写php的测试代码
03 | $redis ->connect( '127.0.0.1' ,6379) or die ( "could net connect redis server" ); |
04 | $query = "select * from test limit 8" ; |
06 | for ( $key = 1; $key < 9; $key ++) |
08 | if (! $redis ->get( $key )) |
10 | $connect = mysql_connect( '127.0.0.1' , 'root' , '123456' ); |
11 | mysql_select_db(mytest); |
12 | $result = mysql_query( $query ); |
13 | //如果没有找到$key,就将该查询sql的结果缓存到redis |
14 | while ( $row = mysql_fetch_assoc( $result )) |
16 | $redis ->set( $row [ 'id' ], $row [ 'name' ]); |
24 | $data [ $key ] = $redis ->get( $key ); |
30 | for ( $key = 1; $key < 9; $key ++) |
32 | echo "number is <b><font color=#FF0000>$key</font></b>" ; |
36 | echo "name is <b><font color=#FF0000>$data[$key]</font></b>" ; |
第一次访问,redis中没有对应的KEY时
![4.png wKiom1UaIrjC3shWAAD2e_5hT48678.jpg](http://static.oschina.net/uploads/img/201602/04085103_G0or.jpg)
再次访问,此时redis中已有相关数据
![5.png wKiom1UaIuTxTtn2AAEcXGxJop8634.jpg](http://static.oschina.net/uploads/img/201602/04085103_gY9D.jpg)
到这里,我们已经实现了redis作为mysql的缓存服务器,但是如果更新了mysql,redis中仍然会有对应的KEY,数据就不会更新,此时就会出现mysql和redis数据不一致的情况。所以接下来就要通过mysql触发器将改变的数据同步到redis中。
六、通过gearman实现同步
1、介绍
Gearman是一个支持分布式的任务分发框架:
Gearman Job Server:Gearman核心程序,需要编译安装并以守护进程形式运行在后台。
Gearman Client:可以理解为任务的请求者。
Gearman Worker:任务的真正执行者,一般需要自己编写具体逻辑并通过守护进程方式运行,Gearman Worker接收到Gearman Client传递的任务内容后,会按顺序处理。
大致流程:
下面要编写的mysql触发器,就相当于 Gearman的客户端。修改表,插入表就相当于直接下发任务。然后通过lib_mysqludf_json UDF库函数将关系数据映射为JSON格式,然后在通过gearman-mysql-udf插件将任务加入到Gearman的任务队列中,最后通过 redis_worker.php,也就是Gearman的worker端来完成redis数据库的更新。
2、安装启动
1 | [root@redis ~] # yum -y install gearmand libgearman-devel |
2 | [root@redis ~] # /etc/init.d/gearmand start |
4 | [root@redis ~] # /etc/init.d/gearmand status |
5 | gearmand (pid 7702) 正在运行... |
3、安装php的gearman扩展
01 | [root@redis ~] # wget -c -t 0 https://pecl.php.net/get/gearman-1.1.1.tgz |
03 | [root@redis ~] # tar xvf gearman-1.1.1.tgz |
04 | [root@redis ~] # cd gearman-1.1.1 |
05 | [root@redis gearman-1.1.1] # phpize |
06 | [root@redis gearman-1.1.1] # ./configure --with-php-config=/usr/bin/php-config |
07 | [root@redis gearman-1.1.1] # make |
08 | [root@redis gearman-1.1.1] # make install |
10 | #如果php的配置文件中没有extension = gearman.so,就加上此行 |
11 | [root@redis ~] # vim /etc/php.ini |
13 | [root@redis ~] # /etc/init.d/php-fpm restart |
![6.png wKiom1UaJ1HheqvfAAC0WqZjDCQ519.jpg](http://static.oschina.net/uploads/img/201602/04085104_2crk.jpg)
这样就是安装成功了
4、安装lib_mysqludf_json
lib_mysqludf_json UDF库函数将关系数据映射为JSON格式。通常,数据库中的数据映射为JSON格式,是通过程序来转换的。
01 | [root@redis ~] # wget -c -t 0 https://github.com/mysqludf/lib_mysqludf_json/archive/master.zip |
02 | [root@redis ~] # unzip master.zip |
03 | [root@redis ~] # cd lib_mysqludf_json-master/ |
04 | [root@redis lib_mysqludf_json-master] # gcc $(mysql_config --cflags) -shared -fPIC -o lib_mysqludf_json.so lib_mysqludf_json.c |
05 | lib_mysqludf_json.c:40:23: 错误:my_global.h:没有那个文件或目录 |
06 | lib_mysqludf_json.c:41:20: 错误:my_sys.h:没有那个文件或目录 |
07 | lib_mysqludf_json.c:43:19: 错误:mysql.h:没有那个文件或目录 |
08 | lib_mysqludf_json.c:44:21: 错误:m_ctype.h:没有那个文件或目录 |
09 | lib_mysqludf_json.c:45:22: 错误:m_string.h:没有那个文件或目录 |
11 | #这里编译报错是因为没有安装mysql的开发包,如果是源码安装的mysql,需要在/etc/ld.so.conf.d/ |
12 | #目录下新建一个文件告诉系统mysql的头文件在哪里 |
13 | [root@redis lib_mysqludf_json-master] # yum -y install mysql-devel |
14 | [root@redis lib_mysqludf_json-master] # gcc $(mysql_config --cflags) -shared -fPIC -o lib_mysqludf_json.so lib_mysqludf_json.c |
16 | mysql> show global variables like 'plugin_dir' ; |
17 | +---------------+-------------------------+ |
18 | | Variable_name | Value | |
19 | +---------------+-------------------------+ |
20 | | plugin_dir | /usr/lib64/mysql/plugin | |
21 | +---------------+-------------------------+ |
24 | [root@redis lib_mysqludf_json-master] # cp lib_mysqludf_json.so /usr/lib64/mysql/plugin/ |
27 | mysql> CREATE FUNCTION json_object RETURNS STRING SONAME 'lib_mysqludf_json.so' ; |
5、安装gearman-mysql-udf
这个插件是用来管理调用 Gearman 的分布式的队列。
01 | [root@redis ~] # wget -c -t 0 https://launchpad.net/gearman-mysql-udf/trunk/0.6/+download/gearman-mysql-udf-0.6.tar.gz |
02 | [root@redis ~] # tar xvf gearman-mysql-udf-0.6.tar.gz |
04 | [root@redis ~] # cd gearman-mysql-udf-0.6 |
05 | [root@redis gearman-mysql-udf-0.6] # ./configure --with-mysql=/usr/bin/mysql_config --libdir=/usr/lib64/mysql/plugin/ |
06 | [root@redis gearman-mysql-udf-0.6] # make |
07 | [root@redis gearman-mysql-udf-0.6] # make install |
10 | mysql> CREATE FUNCTION gman_do_background RETURNS STRING SONAME 'libgearman_mysql_udf.so' ; |
11 | mysql> CREATE FUNCTION gman_servers_set RETURNS STRING SONAME 'libgearman_mysql_udf.so' ; |
14 | mysql> select * from mysql.func; |
15 | +--------------------+-----+-------------------------+----------+ |
16 | | name | ret | dl | type | |
17 | +--------------------+-----+-------------------------+----------+ |
18 | | json_object | 0 | lib_mysqludf_json.so | function | |
19 | | gman_do_background | 0 | libgearman_mysql_udf.so | function | |
20 | | gman_servers_set | 0 | libgearman_mysql_udf.so | function | |
21 | +--------------------+-----+-------------------------+----------+ |
24 | mysql> SELECT gman_servers_set( '127.0.0.1:4730' ); |
6、编写mysql触发器(根据实际情况编写)
2 | CREATE TRIGGER datatoredis AFTER UPDATE ON test FOR EACH ROW BEGIN |
3 | SET @RECV=gman_do_background( 'syncToRedis' , json_object(NEW. id as ` id `, NEW.name as `name`)); |
7、编写gearman的worker端
01 | [root@redis ~] # vim /www/redis_worker.php |
03 | $worker = new GearmanWorker(); |
05 | $worker->addFunction( 'syncToRedis' , 'syncToRedis' ); |
08 | $redis->connect( '127.0.0.1' , 6379); |
10 | while ($worker->work()); |
11 | function syncToRedis($job) |
14 | $workString = $job->workload(); |
15 | $work = json_decode($workString); |
16 | if (!isset($work-> id )){ |
19 | $redis-> set ($work-> id , $work->name); |
24 | [root@redis www] # nohup php redis_worker.php & |
"$redis->set($work->id, $work->name);"这条语句就是将id作KEY和name作VALUE分开存储,需要和前面写的php测试代码的存取一致。
8、更新mysql中的数据
08 | mysql> update test set name = 'ssss' where id = 1; |
从返回值可以看到,触发器是触发成功的(这里的@RECV是上面mysql TIGGER的返回值)。我们在redis中查看数据:
1 | [root@redis redis] # ./redis-cli |
这里的数据居然没有变化,这是我们就要排错了。
01 | [root@redis ~] # vim /var/log/audit/audit.log |
02 | type =AVC msg=audit(1427807674.425:107): avc: denied { name_connect } for pid=12453 comm = "mysqld" dest=4730 scontext=unconfined_u:system_r:mysqld_t:s0 tcontext=system_u:o |
03 | bject_r:port_t:s0 tclass=tcp_socket |
04 | #看到这样一条日志,就知道是selinux阻止了同步 |
06 | #现在将selinux的模式调成Permissive |
07 | [root@redis ~] # getenforce |
09 | [root@redis ~] # setenforce 0 |
10 | [root@redis ~] # getenforce |
设置完成以后,再次执行update,进入redis进行查看
刷新一下刚刚的php界面
![7.png wKiom1UaVVfTHNxBAAEJ_PKtNDU910.jpg](http://static.oschina.net/uploads/img/201602/04085104_OI2e.jpg)
到这里就基本算是大功告成了,只要application将数据写到mysql中,mysql触发器检测到更新,就会通过Gearman将数据同步到redis中。然后读取的话,就直接从redis中进行读取。当然这只是个实验环境,实际上还有很多细节要调整。
参考文章:http://avnpc.com/pages/mysql-replication-to-redis-by-gearman