Redis 的哨兵模式基本已经可以实现高可用,读写分离 ,但是在这种模式下每台 Redis 服务器都存储相同的数据,很浪费内存,所以在redis3.0上加入了 Cluster 集群模式,实现了 Redis 的分布式存储,也就是说每台 Redis 节点上存储不同的内容。
其结构特点:
1、所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
2、节点的fail是通过集群中超过半数的节点检测失效时才生效。
3、客户端与redis节点直连,不需要中间proxy层(负载均衡).客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
4、redis-cluster把所有的物理节点映射到[0-16383]slot(哈希槽,用来存储数据)上(不一定是平均分配),cluster 负责维护node<->slot<->value。
5、Redis集群预分好16384个哈希槽,当需要在 Redis 集群中放置一个 key-value 时,根据 CRC16(key) mod 16384的值,决定将一个key放到哪个桶中。redis集群是无中心化的,指定任一个节点ip都能访问整个集群。每个节点都能读写,但是写的时候会转换到对应哈希槽的节点上。
在一个虚拟机中,默认创建的集群有6个节点(分为三组,每组一主一从,)
集群内部划分为16384个数据分槽,分布在三个主redis(master上)中。
从redis中没有分槽,不会参与集群投票,也不会帮忙加快读取数据,仅仅作为主机的备份。三个主节点中,每个节点中不会存有重复数据,仅仅有自己的从机帮忙冗余。
实验:本次实验在一个虚拟机server1上创建集群总共6个节点。实际生产情况下,每台机器作为一个集群节点。
使用redis源码包create-cluster脚本创建集群(脚本中默认创建6个节点,可更改),每个master节点分配16384个哈希槽,用来存储。redis集群是无中心化的,指定任一个节点ip都能访问整个集群。
查看30001状态为master,对应slave为30004 ,-c 指定集群 -p指定端口
Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽.集群的每个master节点负责一部分哈希槽。此结构容易添加或者删除节点. 如果想添加新节点, 只需要从其他节点分部分哈希槽到新节点上. 如果想移除某节点,需要将此节点中的哈希槽移到其他节点上,然后将此节点从集群中移除. 哈希槽在节点中相互转移不会停止服务,因此添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态.
添加key值
Redis cluster集群的故障迁移
停掉30002做测试:自动将30002的slave30005 切换成master,并接管其哈希槽,只要16384个哈希槽不出错集群就能正常运行。当重新取启动30002时,其自动作为30005的slave。如果master挂掉,其slave成为master接管哈希槽集群可以正常工作,但当master的slave全都挂掉时,没有节点接受此哈希槽,导致集群不可用
再添加两个节点,只需要更改集群创建的脚本文件节点个数NODES:
添加30007节点为master:
--cluster集群 add-node添加节点到集群 添加30007为master 30001指向要添加进的集群
下面代码参数:从127.0.0.1:30008开始依次表示:节点30008; 指向集群30001; slave状态; 对应master30007的ID
此时master30007上没有哈希槽,迁移哈希槽给30007:all就是均衡的从其他3个节点上移3000个哈希槽到30007上
Redis 提供了不同级别的持久化方式:
- RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储.
- AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾.Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大.
- 如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式.
- 你也可以同时开启两种持久化方式, 在这种情况下, 当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整.
- 最重要的事情是了解RDB和AOF持久化方式的不同,让我们以RDB持久化方式开始
RDB的优点
- RDB是一个非常紧凑的文件,它保存了某个时间点得数据集,非常适用于数据集的备份,比如你可以在每个小时报保存一下过去24小时内的数据,同时每天保存过去30天的数据,这样即使出了问题你也可以根据需求恢复到不同版本的数据集.
- RDB是一个紧凑的单一文件,很方便传送到另一个远端数据中心或者亚马逊的S3(可能加密),非常适用于灾难恢复.
- RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能.
- 与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些.
RDB的缺点
- 如果你希望在redis意外停止工作(例如电源中断)的情况下丢失的数据最少的话,那么RDB不适合你.虽然你可以配置不同的save时间点(例如每隔5分钟并且对数据集有100个写的操作),是Redis要完整的保存整个数据集是一个比较繁重的工作,你通常会每隔5分钟或者更久做一次完整的保存,万一在Redis意外宕机,你可能会丢失几分钟的数据.
- RDB 需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的,可能会导致Redis在一些毫秒级内不能响应客户端的请求.如果数据集巨大并且CPU性能不是很好的情况下,这种情况会持续1秒,AOF也需要fork,但是你可以调节重写日志文件的频率来提高数据集的耐久度.
AOF 优点
- 使用AOF 会让你的Redis更加耐久: 你可以使用不同的fsync策略:无fsync,每秒fsync,每次写的时候fsync.使用默认的每秒fsync策略,Redis的性能依然很好(fsync是由后台线程进行处理的,主线程会尽力处理客户端请求),一旦出现故障,你最多丢失1秒的数据.
- AOF文件是一个只进行追加的日志文件,所以不需要写入seek,即使由于某些原因(磁盘空间已满,写的过程中宕机等等)未执行完整的写入命令,你也也可使用redis-check-aof工具修复这些问题.
- Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
- AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单: 举个例子, 如果你不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。
AOF 缺点
- 对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
- 根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)。
如何选择使用哪种持久化方式?
一般来说, 如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。
如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。
有很多用户都只使用 AOF 持久化, 但我们并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快, 除此之外, 使用 RDB 还可以避免之前提到的 AOF 程序的 bug 。
Note: 因为以上提到的种种原因, 未来我们可能会将 AOF 和 RDB 整合成单个持久化模型。 (这是一个长期计划。) 接下来的几个小节将介绍 RDB 和 AOF 的更多细节。
本实验主要目的,采用redis nosql数据库作为Mysql数据库的缓存,在查找的时候,首先查找redis缓存,如果找到则返回结果;如果在redis中没有找到,那么查找Mysql数据库,找到的话则返回结果并且更新redis;如果没有找到则返回空。对于写入的情况,直接写入mysql数据库,mysql数据库通过触发器及UDF机制自动把变更的内容更新到redis中。
3.1.1 server2修改配置文件变为master:注释掉172.25.254.3此行,变为master
3.1.2 server4作为mysql数据库
安装mariadb-server
3.1.3 之前server1上lnmp架构php版本太高, server3上安装一个带有redis模块的lnmp架构
http占用了80端口,停止掉httpd,重新启动nginx
更改后的/etc/local/nginx/html/test.php文件:
connect('172.25.254.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.254.4','redis','westos');
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 "
";
for ($key = 1; $key < 10; $key++)
{
echo "number is $key";
echo "
";
echo "name is $data[$key]";
echo "
";
}
?>
3.1.4server4上:写文件test.sql,并导入数据库, test.sql中的内容:将1~9键值对,写入test数据库里的test表中。test表会自己创建。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 ;
浏览器上首次访问的是mysql数据库:
然后数据从mysql更新到redis,此后会直接从redis缓存拿数据,访问的是redis
网站访问或者server2redis服务端访问还是原来的数据:
3.3.2 如果删除redis缓存中的数据,然后使用浏览器访问,由于此时redis没有数据,会从mysql数据库访问并更新数据到redis:
redis从数据库更新了数据,浏览器中访问到了新数据:
下面实验解决redis和mysql数据库数据同步问题.实现的修改server4数据库数据时,redis同样自动修改数据。
4 .1gearman介绍
Gearman是一个支持分布式的任务分发框架:
Gearman Job Server:Gearman核心程序,需要编译安装并以守护进程形式运行在后台。
Gearman Client:可以理解为任务的请求者。
Gearman Worker:任务的真正执行者,一般需要自己编写具体逻辑并通过守护进程方式运行,Gearman Worker接收到Gearman Client传递的任务内容后,会按顺序处理。
一个Gearman请求的处理过程涉及三个角色:Client -> Job -> Worker。
Client:请求的发起者,可以是 C,PHP,Perl,MySQL UDF 等等。
(发起者相当于server4上的触发器,server4上更改mysql数据库触发了触发器)
Job:请求的调度者,用来负责协调把 Client 发出的请求转发给合适的 Worker。(在server4上,通过udf函数和触发器把数据发送给server3上的gearman)
Worker:请求的处理者,可以是 C,PHP,Perl 等等。(server3上的gearman,处理数据发送给server2上的redis)
因为 Client,Worker 并不限制用一样的语言,所以有利于多语言多系统之间的集成。
甚至我们通过增加更多的 Worker,可以很方便的实现应用程序的分布式负载均衡架构。
大致流程:
下面要编写的mysql触发器,就相当于Gearman的客户端(在server4上)。修改表,插入表就相当于直接下发任务。然后通过lib_mysqludf_json UDF库函数将关系数据映射为JSON格式,然后在通过gearman-mysql-udf插件将任务加入到Gearman的任务队列中,(前面这写都在server4上)最后通过server3上的redis_worker.php,也就是Gearman的worker端(server3上)来完成redis数据库的更新。mysql到redis要经过节藕,1为了安全性,2当数据量特别大时,利用消息队列节藕
本实验以异步方式进行mysql和redis的数据同步。 分布式分发框架。
过程:mysql数据库(在server4上)上有数据变更,通过udf函数配合触发器(在server4上)把数据发送给4730germand(germand的work端,在server3上),由germand分发给redis(在server2上)。
上一个实验基础:server4作为mysql数据库,server3安装lnmp架构(包括编译redis模块),server2作为redis服务端(要能写数据,必须为master)
本实验在上一个实验基础上,
server3上安装gearman的client客户端,用来访问
server3上也有gearman的worker端:任务的真正执行者,一般需要自己编写具体逻辑并通过守护进程方式运行,Gearman Worker 接收到 Gearman Client 传递的任务内容后,会按顺序处理。
server4是Gearman Job Server: Gearman 核心程序,需要编译安装并以守护进程形式运行在后台。
在server4上,解压lib_mysqludf_json-master.zip,并安装gcc
mariadb-devel依赖
安装lib_mysqludf_json,lib_mysqludf_json UDF 库函数将关系数据映射为 JSON 格式。通常,数据库中的数据映射为 JSON 格式,是通过程序来转换的。查看 mysql 的模块目录:
将
lib_mysqludf_json-master/lib_mysqludf_json.so
模块拷贝到/usr/lib64/mysql/plugin/
插件目录下,注册udf函数,并查看函数。
安装 gearman-mysql-udf,这个插件是用来管理调用 Gearman 的分布式的队列。
编译和安装
在lnmp架构服务器server3上安装 gearman 软件包:
server4 mysql数据库中指定 gearman 的服务信息,将数据给german 的worker端server3:
server4上编写 mysql 触发器(根据实际情况编写)
将原来的test删除,将添加了触发器的test.sql导入数据库
在server3 gearmand的worker端编辑一个worker.php,并且worker.php代码中还用到了gearmand模块,所以还要给php添加gearmand模块:
平滑加载php,php执行worker.php 并打入后台
server4 mysql数据库上更改数据
redis(server2中)中的数据同步更新,浏览器直接访问的redis缓存数据,变更成功。
直接在server2中redis中查看数据
浏览器访问的是redis缓存,数据已经更新
总结:此时完成了redis以异步方式同步mysql中的数据。一旦应用程序将数据写到mysql数据库(server4上)中,mysql触发器(server4上)检测到更新,就会通过Gearman的work端(server3上)将数据同步到redis(server2上)中。后面再次浏览器访问(使用lnmp架构访问,lnmp架构在server3上)就直接从redis中进行读取。