Redis 集群没有使用一致性hash, 而是引入了 哈希槽的概念.
Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽.集群的每个节点负责一部分hash槽,举个例子,比如当前集群有3个节点,那么
节点 A 包含 0 到 5500号哈希槽.
节点 B 包含5501 到 11000 号哈希槽.
节点 C 包含11001 到 16384号哈希槽.
这种结构很容易添加或者删除节点. 比如如果我想新添加个节点D, 我需要从节点 A, B, C中得部分槽到D上. 如果我想移除节点A,需要将A中的槽移到B和C节点上,然后将没有任何槽的A节点从集群中移除即可. 由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态
[root@server1 ~]# /etc/init.d/redis_6379 stop
[root@server1 ~]# mkdir /usr/local/rediscluster
[root@server1 rediscluster]# mkdir 700{1..6}
[root@server1 7001]# vim redis.conf
port 7001
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
pidfile "/usr/local/rediscluster/7001/redis.pid"
logfile "/usr/local/rediscluster/7001/redis.log"
daemonize yes
dir "/usr/local/rediscluster/7001"
#启动
[root@server1 7001]# redis-server redis.conf
#查看进程
ps ax
1332 ? Ssl 0:00 redis-server *:7001 [cluster]
[root@server1 7001]# redis-cli -p 7001
127.0.0.1:7001> info
#Cluster
cluster_enabled:1 ##集群激活
#同理配置7002~7006,并启动
[root@server1 src]# pwd
/root/redis-5.0.3/src
[root@server1 src]# cp redis-trib.rb /usr/local/bin/
[root@server1 src]# yum install -y ruby
[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
[OK] All 16384 slots covered. #看到这个表示16384个槽都分配好了
#查看集群信息
[root@server1 ~]# redis-cli --cluster info 127.0.0.1:7001
##输入其他端口也可以,但是必须输一个端口
[root@server1 ~]# redis-cli -c -p 7001
127.0.0.1:7001> get name
-> Redirected to slot [5798] located at 127.0.0.1:7002
(nil)
127.0.0.1:7002> set name douluo
OK
127.0.0.1:7002> get name
"douluo"
127.0.0.1:7002> quit
[root@server1 ~]# redis-cli -c -p 7005
127.0.0.1:7005> get name
-> Redirected to slot [5798] located at 127.0.0.1:7002
"douluo" #在任意节点都可以获取到信息,但是都会跳转到7002
127.0.0.1:7002> quit
##挂掉7002,key还会保存在7004上
cd /usr/local/rediscluster
[root@server1 rediscluster]# cp 7001/redis.conf 7007/
[root@server1 rediscluster]# cp 7001/redis.conf 7008/
[root@server1 7007]# redis-server redis.conf
[root@server1 7008]# redis-server redis.conf
[root@server1 7008]# redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7001
##添加节点
[root@server1 7008]# redis-cli -c -p 7007
127.0.0.1:7007> cluster nodes ##查看节点信息
[root@server1 ~]# redis-cli --cluster add-node 127.0.0.1:7008 127.0.0.1:7007 --cluster-slave --cluster-master-id 7d3fb70a5e9d098977cc873c3aad6433e2b064b0
##master的id
[root@server1 ~]# redis-cli --cluster info 127.0.0.1:7001 ##查看集群信息
[root@server1 ~]# redis-cli --cluster reshard 127.0.0.1:7007
How many slots do you want to move (from 1 to 16384)? 300 ##随便分配一点
What is the receiving node ID? b9ee016d45dde6a0067dd3e19139f1044a621d77 ##接收哈希槽的节点ID
Please enter all the source node IDs.
Type ‘all’ to use all the nodes as source nodes for the hash slots.
Type ‘done’ once you entered all the source nodes IDs.
Source node #1: all
#从所有节点获取哈希槽
[root@server1 ~]# redis-cli --cluster check 127.0.0.1:7001 ##检查可以看到已经分配哈希槽
[root@server1 ~]# redis-cli --cluster rebalance --cluster-threshold 1 --cluster-use-empty-masters 127.0.0.1:7001 ##均分哈希槽
[root@server1 ~]# redis-cli --cluster check 127.0.0.1:7001
##查看数据[root@server1 ~]# redis-cli -c -p 7008
数据访问流程:client -> app -> redis -> mysql -> redis -> client
客户端用app访问,先在redis里读数据,速度快,redis没有才去mysql读,读完保存在redis里,然后返回客户端,下次获取数据回更快
##首先在server1配置好nginx.修改配置文件
location / {
root html;
index index.php index.html index.htm;
}
...
location ~ \.php$ {
root html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
#fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
include fastcgi.conf;
}
#启动并测试nginx
/etc/init.d/redis_6379 restart
#删除原来的key
[root@server2 ~]# redis-cli
127.0.0.1:6379> get name
"dd"
127.0.0.1:6379> DEL name
(integer) 1
127.0.0.1:6379> get name
(nil)
##开启server3,关闭原来的mysql
[root@server3 ~]# rpm -qa | grep mysql
mysql-community-libs-5.7.24-1.el7.x86_64
mysql-community-server-5.7.24-1.el7.x86_64
mha4mysql-node-0.58-0.el7.centos.noarch
mysql-community-common-5.7.24-1.el7.x86_64
mysql-community-client-5.7.24-1.el7.x86_64
mysql-community-libs-compat-5.7.24-1.el7.x86_64
##卸载原来的mysql
[root@server3 ~]# rpm -e `rpm -qa | grep mysql` --nodeps
#安装mariadb,这里试验用这个就行
[root@server3 ~]# yum install -y mariadb-server
#清除原来数据目录里的内容
[root@server3 ~]# cd /var/lib/mysql
[root@server3 mysql]# rm -fr *
#启动mariadb
[root@server3 mysql]# systemctl start mariadb
#安全初始化
[root@server3 ~]# mysql_secure_installation ##这里密码可以设置为简单的,如redhat
#登录数据库,授权用户
MariaDB [(none)]> create database test;
Query OK, 1 row affected (0.00 sec)
MariaDB [(none)]> grant all on test.* to redis@'%' identified by 'redhat';
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]> flush privileges;
Query OK, 0 rows affected (0.00 sec)
cat 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
[root@server1 ~]# mv test.php /usr/local/nginx/html/
[root@server1 html]# mv test.php index.php
[root@server1 ~]# cd /usr/local/nginx/html/
[root@server1 html]# cat index.php
connect('172.25.61.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))
{ ##连接server3的mysql
$connect = mysql_connect('172.25.61.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 "
";
for ($key = 1; $key < 10; $key++)
{
echo "number is $key";
echo "
";
echo "name is $data[$key]";
echo "
";
}
?>
#浏览器访问测试
172.25.61.1
可以看到导入的数据
MariaDB [test]> update test set name='westos' where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
127.0.0.1:6379> set 2 redhat
OK
[root@server1 ~]# systemctl start gearmand ##之前已经安装过
unzip lib_mysqludf_json-master.zip
在server3上解压lib_mysqludf_json-master.zip
#安装mariadb-devel
[root@server3 lib_mysqludf_json-master]# yum install -y mariadb-devel.x86_64
#编译模块(文档上有)
[root@server3 lib_mysqludf_json-master]# gcc $(mysql_config --cflags) -shared -fPIC -o lib_mysqludf_json.so lib_mysqludf_json.c
#将模块放到mysql插件目录
[root@server3 ~]# cp lib_mysqludf_json-master/lib_mysqludf_json.so /usr/lib64/mysql/plugin/
#在server3上查看
MariaDB [(none)]> show global variables like 'plugin_dir';
+---------------+--------------------------+
| Variable_name | Value |
+---------------+--------------------------+
| plugin_dir | /usr/lib64/mysql/plugin/ |
+---------------+--------------------------+
1 row in set (0.00 sec)
#注册udf函数
MariaDB [(none)]> CREATE FUNCTION json_object RETURNS STRING SONAME 'lib_mysqludf_json.so';
[root@server3 ~]# tar zxf gearman-mysql-udf-0.6.tar.gz
#先安装libgearman,从server1上scp
[root@server3 ~]# yum install -y libevent-devel-2.0.21-4.el7.x86_64.rpm libgearman-
#编译安装gearman插件
[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
#注册udf函数
mysql -p
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)
##指定gman服务信息
MariaDB [(none)]> SELECT gman_servers_set('172.25.61.1:4730');
##编写mysql触发器
[root@server3 ~]# cat 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 -p < test.sql
##查看触发器
MariaDB [(none)]> SHOW TRIGGERS FROM test;
##编写gman的worker端
[root@server1 redis]# cp worker.php /usr/local/
[root@server1 redis]# cd /usr/local/
cat worker.php
addServer();
$worker->addFunction('syncToRedis', 'syncToRedis');
$redis = new Redis();
$redis->connect('127.0.61.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);
}
?>
##后台运行worker
[root@server1 ~]# nohup php /usr/local/worker.php &> /dev/null &
##修改数据库内容
MariaDB [test]> update test set name='redhat' where id=2;
##在redis上查看
127.0.0.1:6379> get 2
"redhat"