一、概述
Redis的集群主要是使用切片技术来搭建的,简单来说就是把所有KEY分散存放到不同的redis节点上(不要把鸡蛋都放在一个篮子里)。
1. 集群基本原理
Redis集群中内置了16384个槽位,当需要放置数据时,Redis先对KEY使用CRC16算法计算出一个结果,然后把结果对16384求余数,这样每个KEY都会对应一个编号在0—16383之间的槽号码,Redis会根据节点数量大致均等的原则将哈希槽映射到不同节点。比如有3个Redis节点,把16384分成3段,每个节点承担一段范围的哈希槽。
好处是添加和移除节点非常容易。比如要新增一个节点,值需要从其他节点上移动一些槽位到新节点即可,如果要删除一个节点,那就把它拥有的槽位移动到其他节点,然后进行删除。上面这种移动过程不需要任何停机时间。
- 所有的redis节点彼此互通
- 节点的失败是通过集群中超过半数的节点检测失效才生效的
- 客户端与Redis节点直连,不需要中间的代理,客户端不需要连接集群所有节点,只需要连接任何一个可用的节点即可
Redis集群是通过分片的方式来保存数据库中的键值对,也就是把所有键值对分摊的放在0—16383之间的槽位上。在一个集群中16384个槽位必须同时在线否则集群就会失败。
2. 什么时候集群不可用
- 集群中的任意Master失败,且当前的Master没有Slave,则集群进入失败状态,其实就是整体的0—16383这些槽不完整。
- 如果集群中超过半数的Master失败,无论是否有slave,集群宣告失败
当集群不可用时,所有对集群的操作都不可执行
二、集群搭建
相关配置接前文:https://blog.csdn.net/meltsnow/article/details/91429711
1.server1将之前的redis关闭
[root@server1 ~]# /etc/init.d/redis_6379 stop
2.切换到/usr/local目录,创建一个rediscluster目录
[root@server1 ~]# cd /usr/local
[root@server1 local]# mkdir rediscluster
[root@server1 local]# ls
切换到新建目录rediscluster,再这个目录下创建6个子目录
[root@server1 local]# cd rediscluster/
[root@server1 rediscluster]# ls
[root@server1 rediscluster]# mkdir 700{1..6}
[root@server1 rediscluster]# ls
7001 7002 7003 7004 7005 7006
3.创建配置文件,让配置文件生效
[root@server1 rediscluster]# cd 7001
[root@server1 7001]# ls
[root@server1 7001]# vim redis.conf
[root@server1 7001]#
[root@server1 7001]# redis-server redis.conf
1 port 7001
2 cluster-enabled yes
3 cluster-config-file nodes.conf
4 cluster-node-timeout 5000
5 appendonly yes
6 pidfile "/usr/local/rediscluster/7001/redis.pid"
7 logfile "/usr/local/rediscluster/7001/redis.log"
8 daemonize yes
9 dir "/usr/local/rediscluster/7001"
查看进程可以看到,进程已经开启
[root@server1 7001]# ps ax
端口也已经打开
[root@server1 7001]# netstat -tnlp
4.将7001编辑好的配置文件复制给每个目录中,修改相应的端口号和文件名
[root@server1 7001]# cp redis.conf ../7002/
[root@server1 7001]# cp redis.conf ../7003/
[root@server1 7001]# cp redis.conf ../7004/
[root@server1 7001]# cp redis.conf ../7005/
[root@server1 7001]# cp redis.conf ../7006/
每一个目录编辑完之后再当前目录生效配置文件(目录7002下对应的文件)
1 port 7002
2 cluster-enabled yes
3 cluster-config-file nodes.conf
4 cluster-node-timeout 5000
5 appendonly yes
6 pidfile "/usr/local/rediscluster/7002/redis.pid"
7 logfile "/usr/local/rediscluster/7002/redis.log"
8 daemonize yes
9 dir "/usr/local/rediscluster/7002"
可以使用vim替换操作【:%s/7001/7002/g】
修改完毕使文件生效
redis-server redis.conf
5.查看进程
[root@server1 7006]# ps ax
6.切换到redis安装目录下,将src/redis-trib.rb文件复制到/usr/local/bin目录下,复制之后redis命令可以自动补齐
[root@server1 ~]# cd redis-5.0.3
[root@server1 redis-5.0.3]# cd src/
[root@server1 src]# ls
[root@server1 src]# cp redis-trib.rb /usr/local/bin
7.创建集群
[root@server1 ~]# redis-cli --cluster create --cluster-replicas 1 127.0.0.1:7001 127.0.0.1:7002
127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006
可以看到7001,7002和7003是master,对应的slave分别是7004,7005,7006
测试:
1.查看集群节点间对应关系
[root@server1 ~]# redis-cli --cluster info 127.0.0.1:7001
[root@server1 ~]# redis-cli --cluster info 127.0.0.1:7002
[root@server1 ~]# redis-cli --cluster info 127.0.0.1:7003
2.查看节点状态信息
[root@server1 ~]# redis-cli -c -p 7001
127.0.0.1:7001> info
上传信息之后自动转到7002
127.0.0.1:7001> set name peng
-> Redirected to slot [5798] located at 127.0.0.1:7002
OK
127.0.0.1:7002> get name
"peng"
127.0.0.1:7002> quit
2.无中心检测
[root@server1 ~]# redis-cli -c -p 7004
127.0.0.1:7004> info
查看数据后又转到7002
127.0.0.1:7004> get name
-> Redirected to slot [5798] located at 127.0.0.1:7002
"peng"
127.0.0.1:7002> quit
3.将7002宕掉,对应的slave7005会接替
[root@server1 ~]# redis-cli -c -p 7002
127.0.0.1:7002> shutdown
not connected>
not connected>
[root@server1 ~]# redis-cli --cluster info 127.0.0.1:7001
再将这个接替的master宕掉,集群会崩溃
[root@server1 ~]# redis-cli -c -p 7005
127.0.0.1:7005> SHUTDOWN
not connected>
[root@server1 ~]# redis-cli --cluster info 127.0.0.1:7001
[root@server1 ~]# redis-cli -c -p 7001
恢复集群:
重新加载7002和7005的配置文件即可
[root@server1 ~]# cd /usr/local/rediscluster/7002
[root@server1 7002]# ls
appendonly.aof dump.rdb nodes.conf redis.conf redis.log
[root@server1 7002]# redis-server redis.conf
[root@server1 7002]# cd ../7005
[root@server1 7005]# redis-server redis.conf
[root@server1 7005]#
再次查看集群,已经恢复
[root@server1 7005]# redis-cli --cluster info 127.0.0.1:7001
查看节点信息状态,查看数据可以查看,查看后自动转到7005
[root@server1 7006]# redis-cli -c -p 7001
三、向已经存在的集群中添加节点
1.切换到/usr/local/rediscluster目录
[root@server1 ~]# cd /usr/local/rediscluster/
[root@server1 rediscluster]# ls
7001 7002 7003 7004 7005 7006
2.创建两个新的节点目录 7007 和7008
[root@server1 rediscluster]# mkdir 7007
[root@server1 rediscluster]# mkdir 7008
[root@server1 rediscluster]# ls
7001 7002 7003 7004 7005 7006 7007 7008
3.将7001的配置文件复制给两个新建的节点,编辑配置文件,拉起节点
[root@server1 rediscluster]# cp 7001/redis.conf 7007/
[root@server1 rediscluster]# cp 7001/redis.conf 7008/
[root@server1 rediscluster]# cd 7007
[root@server1 7007]# vim redis.conf
[root@server1 7007]# redis-server redis.conf
[root@server1 7007]# cd ../7008/
[root@server1 7008]# vim redis.conf
[root@server1 7008]# redis-server redis.conf
4.查看进程可以看到两个节点已经起来
[root@server1 7008]# ps ax
5.将节点添加进集群
[root@server1 7008]# redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7001
检查集群:显示7007已经在集群中,但是没有slave
[root@server1 7008]# redis-cli --cluster check 127.0.0.1:7001
6.将7008作为7007的slave端加入进集群
[root@server1 7008]# redis-cli --cluster add-node 127.0.0.1:7008 127.0.0.1:7007
--cluster-slave --cluster-master-id 46fb40a3874205d57bd8945a2c1611f9ca136fce
再次检查集群:7007和7008都加入到集群中,7007是master,7008是slave
[root@server1 7008]# redis-cli --cluster check 127.0.0.1:7001
7.查看集群信息,可以看到有四个master,每一个master都有一个slave,但是7007的哈希槽是0
[root@server1 7008]# redis-cli --cluster info 127.0.0.1:7001
给新添加的节点分配哈希槽
(1)手动分配:想分配多少个就分配多少个,但由于集群节点中的哈希槽不相同可能会导致数据不一致,存储出问题
[root@server1 7008]# redis-cli --cluster reshard --cluster-from all --cluster-to
46fb40a3874205d57bd8945a2c1611f9ca136fce --cluster-slots 300 --cluster-yes 127.0.0.1:7001
参数解释:
检查:7007有300个哈希槽
[root@server1 7008]# redis-cli --cluster check 127.0.0.1:7001
(2)平均分配,系统自动重新平均分配,分配后每一个master的哈希槽数量是一样的
[root@server1 7008]# redis-cli --cluster rebalance --cluster-threshold 1
--cluster-use-empty-masters 127.0.0.1:7001
检查:
[root@server1 7008]# redis-cli --cluster check 127.0.0.1:7001
测试:
查看7008结点的信息和状态
可以看到它是一个slave,它的master是7007
[root@server1 7008]# redis-cli -c -p 7008
127.0.0.1:7008> info
获取数据,可以查看到,查看完之后会自动跳转到7007
127.0.0.1:7008> get name
-> Redirected to slot [5798] located at 127.0.0.1:7007
"peng"
127.0.0.1:7007>
四、redis结合lnmp架构做mysql的缓存服务器
工作流程:client -> app -> redis -> mysql(如果在redis找不到数据在访问mysql) -> redis -> client
主机名 | IP | 服务 |
---|---|---|
server1 | 172.25.6.1 | nginx,php |
server2 | 172.25.6.2 | redis |
server3 | 172.25.6.3 | mysql |
准备工作
- 将之前的集群中所有的节点都关掉
- 将server1替换掉,换一个环境纯净的虚拟机
1.替换server1
[root@foundation6 ~]# cd /var/lib/libvirt/images/
[root@foundation6 images]# qemu-img create -f qcow2 -b peng703.qcow2 vm1
2.server1安装nginx
(1)解压安装包
[root@server1 ~]# ls
nginx-1.16.0.tar.gz
[root@server1 ~]# tar zxf nginx-1.16.0.tar.gz
[root@server1 ~]# ls
nginx-1.16.0 nginx-1.16.0.tar.gz
[root@server1 ~]# cd nginx-1.16.0
[root@server1 nginx-1.16.0]#
(2)安装依赖性
[root@server1 nginx-1.16.0]# yum install -y gcc zlib-devel pcre-devel
(3)关闭debug日志
[root@server1 nginx-1.16.0]# vim auto/cc/gcc
(4)编译安装
[root@server1 nginx-1.16.0]# ./configure --prefix=/usr/local/nginx
[root@server1 nginx-1.16.0]# make && make install
(5)编辑配置文件,打开php,添加默认测试页index.php
[root@server1 ~]# cd /usr/local/nginx
[root@server1 nginx]# ls
conf html logs sbin
[root@server1 nginx]# vim conf/nginx.conf
[root@server1 nginx]#
[root@server1 nginx]# sbin/nginx -t
(6)打开服务,可以看到80端口打开
[root@server1 nginx]# sbin/nginx
[root@server1 nginx]# netstat -tnlp
3.安装php
(1)下载php以及需要的依赖性
[root@server1 ~]# ls
nginx-1.16.0 nginx-1.16.0.tar.gz rhel7
[root@server1 ~]# cd rhel7/
(2)安装所有的包
[root@server1 ~]# cd rhel7/
[root@server1 rhel7]# ls
[root@server1 rhel7]# yum install -y *
(3)开启php服务,查看端口号9000已经打开
[root@server1 rhel7]# systemctl start php-fpm
[root@server1 rhel7]#
[root@server1 rhel7]# netstat -tnlp
4.在/usr/local/nginx/html目录下编写一个测试页index.php
[root@server1 ~]# cd /usr/local/nginx/html/
[root@server1 html]# ls
50x.html index.html
[root@server1 html]# vim index.php
[root@server1 html]# ls
50x.html index.html index.php
以下是测试页index.php相关内容
<?php
$redis = new Redis();
$redis->connect('172.25.6.2',6379) or die ("could net connect redis server");
# $query = "select * from test limit 9";
$query = "select * from test";
for ($key = 1; $key < 10; $key++)
{
if (!$redis->get($key))
{
$connect = mysql_connect('172.25.6.3','redis','redhat');
mysql_select_db(test);
$result = mysql_query($query);
//如果没有找到$key,就将该查询sql的结果缓存到redis
while ($row = mysql_fetch_assoc($result))
{
$redis->set($row['id'],$row['name']);
}
$myserver = 'mysql';
break;
}
else
{
$myserver = "redis";
$data[$key] = $redis->get($key);
}
}
echo $myserver;
echo "<br>";
for ($key = 1; $key < 10; $key++)
{
echo "number is <b><font color=#FF0000>$key</font></b>";
echo "<br>";
echo "name is <b><font color=#FF0000>$data[$key]</font></b>";
echo "<br>";
}
?>
重新加载nginx服务。
[root@server1 html]# ../sbin/nginx -s reload
配置server2
因为之前安装配置过redis,server2的redis是一个slave,所以编辑配置文件,删除slave的配置即可
1.编辑配置文件,删除最后一行
[root@server2 ~]# cd /etc/redis/
[root@server2 redis]# ls
6379.conf sentinel.conf
[root@server2 redis]# vim 6379.conf
[root@server2 redis]#
2.开启redis服务
[root@server2 redis]# /etc/init.d/redis_6379 start
Starting Redis server...
3.删除之前的数据,查看当前状态
[root@server2 ~]# redis-cli
127.0.0.1:6379> get name
"peng"
127.0.0.1:6379> del name
(integer) 1
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> info
配置server3
1.关闭mysqld服务,卸载mysql(如果安装过的话)
[root@server3 ~]# systemctl stop mysqld
[root@server3 ~]#
[root@server3 ~]# rpm -e `rpm -qa | grep mysql` --nodeps
warning: /etc/my.cnf saved as /etc/my.cnf.rpmsave
2.安装mariadb
[root@server3 ~]# yum install -y mariadb-server
3.开启mariadb
开启前保证该目录清空
[root@server3 ~]# cd /var/lib/mysql/
[root@server3 mysql]# ls
[root@server3 mysql]# systemctl start mariadb
4.安全初始化
[root@server3 mysql]# mysql_secure_installation
5.登陆数据库,创建test库,创建redis用户并授权
[root@server3 mysql]# mysql -p
Enter password:
MariaDB [(none)]> show databases;
MariaDB [(none)]> create database test;
MariaDB [(none)]> grant all on test.* to redis@'%' identified by 'redhat';
##用户授权,用户名为redis,密码为redhat
MariaDB [(none)]> flush privileges;
6.将test.sql倒入数据库
[root@server3 ~]# ls
redis-5.0.3 test.sql
[root@server3 ~]# mysql -predhat < test.sql
7.查看test库中的数据
访问测试:
打开浏览器访问server1的ip访问到的是mysql
刷新一下访问到的是redis,之后在刷新访问的一直是redis
命令行访问测试:
[root@server2 redis]# redis-cli
127.0.0.1:6379> get 1
"test1"
127.0.0.1:6379> get 2
"test2"
.补充:
test.sql 的内容如下:
[root@server3 ~]# vim test.sql
分布式的任务分发管理器
通过server1分发任务
数据库当前不支持实时更新数据加一个函数
修改数据后再测试:
1.server3在数据库中修改一个数据
MariaDB [test]> update test set name='westos' where id='1';
2.server2中redis查看 没有改变,浏览器查看也没有改变
[root@server2 redis]# redis-cli
127.0.0.1:6379> get 1
"test1"
3.server2再修改一个数据,浏览器查看改变了,但是数据库中没有改变
[root@server2 redis]# redis-cli
127.0.0.1:6379> set 2 westos
OK
127.0.0.1:6379> get 2
"westos"
总结:当前情况下,在mysql端或者redis端改变数据,在浏览器中查看不会同步改变,在没有修改的另一端查看数据也不会同步,这样在实际应用中显然是不合理的,,接下来我们将提供解决方案
五、配置gearman实现数据同步
同步过程:mysql -> update -> trigger -> json_map -> mysql plugin -> gearman(server) -> worker(php-gearman/php-redis) -> redis
mysql 触发器,就相当于 Gearman 的客户端。修改表,插入表就相当于直接下发任务。然后通过 lib_mysqludf_json UDF 库函数将关系数据映射为 JSON 格式,然后在通过 gearman-mysql-udf 插件将任务加入到 Gearman 的任务队列中,最后通过redis_worker.php,也就是 Gearman 的 worker 端来完成 redis 数据库的更新
- 1.Gearman 是一个支持分布式的任务分发框架
- 2.Gearman Job Server:Gearman 核心程序,需要编译安装并以守护进程形式运行在后台
- 3.Gearman Client:可以理解为任务的请求者
- 4.Gearman Worker:任务的真正执行者,一般需要自己编写具体逻辑并通过守护进程方式运行,Gearman Worker 接收到 Gearman Client 传递的任务内容后,会按顺序处理
server1配置如下
1.开启gearmand服务(之前在安装php的时候我们已经安装过gearman的包)
[root@server1 html]# systemctl start gearmand
[root@server1 html]# systemctl status gearmand
(server3)mysql端配置如下:
1.下载一个lib_mysqludf_json-master的包,安装unzip命令,解压安装包
[root@server3 ~]# ls
lib_mysqludf_json-master.zip redis-5.0.3 test.sql
[root@server3 ~]# yum install -y unzip
[root@server3 ~]# ls
lib_mysqludf_json-master.zip redis-5.0.3 test.sql
[root@server3 ~]# unzip lib_mysqludf_json-master.zip
[root@server3 ~]# ls
2.安装依赖性,安装lib_mysqludf_json
(1)切换到安装目录
[root@server3 ~]# ls
lib_mysqludf_json-master lib_mysqludf_json-master.zip redis-5.0.3 test.sql
[root@server3 lib_mysqludf_json-master]# ls
(2)安装依赖性
[root@server3 lib_mysqludf_json-master]# yum install -y mariadb-devel
[root@server3 lib_mysqludf_json-master]# yum install -y gcc
(3)gcc编译
[root@server3 lib_mysqludf_json-master]# gcc $(mysql_config --cflags) -shared -fPIC -o lib_mysqludf_json.so lib_mysqludf_json.c
(4)将lib_mysqludf_json.so模块复制到mysql插件库中
[root@server3 lib_mysqludf_json-master]# cp lib_mysqludf_json.so /usr/lib64/mysql/plugin/
(5)登陆数据库可以查看到插件目录
[root@server3 lib_mysqludf_json-master]# mysql -p
Enter password:
MariaDB [(none)]> show global variables like 'plugin_dir';
3.安装gearman软件包,安装php的gearman扩展程序
(1)解压gearman-mysql-udf安装包
[root@server3 ~]# ls
gearman-mysql-udf-0.6.tar.gz lib_mysqludf_json-master.zip test.sql
lib_mysqludf_json-master redis-5.0.3
[root@server3 ~]# tar zxf gearman-mysql-udf-0.6.tar.gz
(2)server1将libevent-devel-2.0.21-4.el7.x86_64.rpm, libgearman-1.1.12-18.el7.x86_64.rpm , libgearman-devel-1.1.12-18.el7.x86_64.rpm 三个包传给server3
[root@server1 rhel7]# scp libevent-devel-2.0.21-4.el7.x86_64.rpm libgearman-* server3:
(3)server3安装这三个包
[root@server3 ~]# yum install -y libevent-devel-2.0.21-4.el7.x86_64.rpm libgearman-*
(4)编译安装
[root@server3 ~]# cd gearman-mysql-udf-0.6
[root@server3 gearman-mysql-udf-0.6]# ls
[root@server3 gearman-mysql-udf-0.6]# ./configure --libdir=/usr/lib64/mysql/plugin/ --with-mysql
[root@server3 gearman-mysql-udf-0.6]# make && make install
4.注册udf函数,查看函数
MariaDB [(none)]> CREATE FUNCTION json_object RETURNS STRING SONAME 'lib_mysqludf_json.so';
MariaDB [(none)]> CREATE FUNCTION gman_do_background RETURNS STRING SONAME 'libgearman_mysql_udf.so';
MariaDB [(none)]> CREATE FUNCTION gman_servers_set RETURNS STRING SONAME 'libgearman_mysql_udf.so';
MariaDB [(none)]> select * from mysql.func;
+--------------------+-----+-------------------------+----------+
| name | ret | dl | type |
+--------------------+-----+-------------------------+----------+
| json_object | 0 | lib_mysqludf_json.so | function |
| gman_do_background | 0 | libgearman_mysql_udf.so | function |
| gman_servers_set | 0 | libgearman_mysql_udf.so | function |
+--------------------+-----+-------------------------+----------+
3 rows in set (0.00 sec)
5.指定gearman服务信息
MariaDB [(none)]> select gman_servers_set('172.25.6.1:4730');
+-------------------------------------+
| gman_servers_set('172.25.6.1:4730') |
+-------------------------------------+
| 172.25.6.1:4730 |
+-------------------------------------+
1 row in set (0.00 sec)
6.编写触发器,导入数据库
[root@server3 ~]# vim test.sql
use test;
#CREATE TABLE `test` (`id` int(7) NOT NULL AUTO_INCREMENT, `name` char(8) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
#INSERT INTO `test` VALUES (1,'test1'),(2,'test2'),(3,'test3'),(4,'test4'),(5,'test5'),(6,'test6'),(7,'test7'),(8,'test8'),(9,'test9');
DELIMITER $$
CREATE TRIGGER datatoredis AFTER UPDATE ON test FOR EACH ROW BEGIN
SET @RECV=gman_do_background('syncToRedis', json_object(NEW.id as `id`, NEW.name as `name`));
END$$
DELIMITER ;
[root@server3 ~]# mysql -predhat < test.sql
7.查看触发器
配置worker
1.编写gearman的worker端
[root@server1 ~]# vim worker.php
<?php
$worker = new GearmanWorker();
$worker->addServer();
$worker->addFunction('syncToRedis', 'syncToRedis');
$redis = new Redis();
$redis->connect('172.25.6.2', 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, $work->name);
}
?>
[root@server1 ~]# cp worker.php /usr/local
2.后台运行worker
[root@server1 ~]# nohup php /usr/local/worker.php &>/dev/null &
[1] 14486
[root@server1 ~]#
[root@server1 ~]# ps ax
测试:
mysql修改数据
浏览器中刷新,就可以看到数据更新,此时显示访问的是redis端,这说明数据已经同步更新
redis查看也可以看到