redis分布式集群数据库
1.介绍
redis 是一个高性能的 key-value 数据库。 redis 的出现,很大程度补偿了memcached 这类 keyvalue 存储的不足,在部分场合可以对关系数据库起到很好的补充作用。它提供了Python,Ruby,Erlang,PHP 客户端,使用很方便。Redis 的所有数据都是保存在内存中,然后不定期的通过异步方式保存到磁盘上(这称为“半持久化模式”);也可以把每一次数据变化都写入到一个 append only file(aof)里面(这称为“全持久化模式”)。
2.环境:
rhel6.5 selinx and iptales disabled
master:server1-172.25.4.1
slave: server4-172.25.4.4
3.redis安装
master 和slvae都做下面操作
tar zxf redis-3.0.2.tar.gz
cd redis-3.0.2
yum install gcc -y
make
make install
cd redis-3.0.2/utils
./install_server.sh #全部使用默认
# ls /usr/local/bin/
redis-benchmark redis-check-dump redis-sentinel
redis-check-aof redis-cli
redis-server
这些可执行文件的作用如下:
redis-server: Redis 服务主程序。
redis-cli: Redis 客户端命令行工具,也可以用 telnet 来操作。redis-benchmark: Redis 性能测试工具,用于测试读写性能。
redis-check-aof:检查 redis aof 文件完整性,aof 文件持久化记录服务器执行的所有写
操作命令,用于还原数据。
redis-check-dump:检查 redis rdb 文件的完整性,rdb 就是快照存储, 即按照一定的
策略周期性的将数据保存到磁盘,是默认的持久化方式。
redis-sentinel:
redis-sentinel 是集群管理工具,主要负责主从切换。
4. Redis 客户端使用
redis-cli 命令行
127.0.0.1:6379> config get *
1) "dbfilename"
2) "dump.rdb"
3) "requirepass"
4) ""
5) "masterauth"
你可以通过修改 redis.conf 文件或使用 CONFIG set 命令来修改配置,但重启服务后会读取 redis.conf 文件配置。
[root@server1 ~]# redis-cli
127.0.0.1:6379> CONFIG GET loglevel1) "loglevel"
2) "notice"
127.0.0.1:6379> CONFIG SET loglevel "debug"
OK
127.0.0.1:6379> quit
5.redis集群的基本搭建
slave上
vim /etc/redis/6379.conf
207 slaveof 172.25.4.1 6379
/etc/init.d/redis_6379 restart
然后在master上
redis-cli
127.0.0.1:6379> set name redhat
OK
127.0.0.1:6379> set pass westos
OK
在slave上
redis-cli
127.0.0.1:6379> get name
"redhat"
127.0.0.1:6379> get pass
"westos"
6.sentinel配置
master上
cp /redis-3.0.2/sentinel.conf /etc/redis/
vim /etc/redis/sentinel.conf
port 26379
dir /tmp
sentinel monitor mymaster 172.25.4.1 6379 2
sentinel down-after-milliseconds mymaster 10000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
scp /etc/redis/sentinel.conf 172.25.4.4:/etc/redis/ #必须在启动sentinel之前复制,不然启动以后配置文件就会自动修改
scp /etc/redis/sentinel.conf 172.25.4.2:/etc/redis/
然后在每个节点打开sentinel
redis-server /etc/redis/sentinel.conf --sentinel
就会发现所有的节点都已经加上了
测试:
在master上
redis-cli
127.0.0.1:6379> shutdown
not connected> quit
在slave上就会发现,172.25.4.1已经下线了,然后开始投票,选出新的master
+switch-master mymaster 172.25.4.1 6379 172.25.4.2 6379
172.25.4.1变成了slave,并且时down状态
+sdown slave 172.25.4.1:6379 172.25.4.1 6379 @ mymaster 172.25.4.2 6379
如果要打开172.25.4.1,就去
vim /etc/redis/6379.conf
207 slaveof 172.25.4.2 6379
/etc/init.d/redis_6379 restart 重启服务,172.25.4.1就加入了slave
-sdown slave 172.25.4.1:6379 172.25.4.1 6379 @ mymaster 172.25.4.2 6379
重新开一个终端
redis-cli -h 172.25.4.2 -p 26379
172.25.4.2:26379> info
master0:name=mymaster,status=ok,address=172.25.4.2:6379,slaves=2,sentinels=3
或者
redis-cli -h 172.25.4.2 -p 6379
172.25.4.2:6379> info replication
slave0:ip=172.25.4.4,port=6379,state=online,offset=278736,lag=1
slave1:ip=172.25.4.1,port=6379,state=online,offset=278736,lag=1
可以查看你的master,还有slave数,还有你的sentinels数
7.多个sentinels管理
mkdir /usr/local/cluster
cd /usr/local/cluster
mkdir 3000{1,2,3,4,5,6}
cd 30001/
vim redis.conf
daemonize yes
pidfile "/usr/local/cluster/30001/redis.pid"
port 30001
logfile "/usr/local/cluster/30001/redis.log"
dir "/usr/local/cluster/30001"
cluster-enabled yes
cluster-config-file nodes-30001.conf
cluster-node-timeout 15000
appendonly yes
cd /usr/local/cluster
cp 3001/redis.conf 30002/
cp 3001/redis.conf 30003/
cp 3001/redis.conf 30004/
cp 3001/redis.conf 30005/
cp 3001/redis.conf 30006/
把里面的配置内容修改 >> vim 编辑模式下,%s/30001/30002/g
redis-server 30001/redis.conf #启动所有服务
cd /root/redis-3.0.2/src
cp redis-trib.rb /usr/local/bin/
yum install -y rubygems-1.3.7-5.el6.noarch.rpm
gem install redis-3.3.1.gem
redis-trib.rb create --replicas 1 127.0.0.1:30001 127.0.0.1:30002 127.0.0.1:30003 127.0.0.1:30004 127.0.0.1:30005 127.0.0.1:30006
redis-cli -p 30001
127.0.0.1:30001> cluster info
127.0.0.1:30001> cluster nodes
[root@server1 ~]# redis-cli
127.0.0.1:6379> get user1
"123"
127.0.0.1:6379> get user2
"456"
127.0.0.1:6379> get user3
"789"
redis-trib.rb import --from 127.0.0.1:6379 127.0.0.1:30001
如果同shutdown掉一半的master节点,整个集群就会挂掉
redis-cli -c -p 30004
127.0.0.1:30004> get name
(error) CLUSTERDOWN The cluster is down
1-4 2-5 3-6 如果down掉一个master节点,他对应的slave节点会接管他
添加slave节点到某master的管理里面
redis-trib.rb add-node --slave --master-id a76de1df1fe4d2d7eb0ca4f2b38f427682a7d93a 127.0.0.1:30008 127.0.0.1:30001
删除节点
redis-trib.rb del-node 127.0.0.1:30001
a76de1df1fe4d2d7eb0ca4f2b38f427682a7d93a
删除数据库rpm包
rpm -e --nodeps `rpm -qa|grep mysql`
cd /var/lib/mysql
rm -fr *
yum install mysql-server -y
8.redis结合lnmp架构
Redis 作 mysql 的缓存服务器
1. 安装 lnmp 环境,安装以下软件包:
nginx php php-fpm php-cli php-common php-gd php-mbstring php-mysql
php-pdo php-devel mysql mysql-server
http://download.fedoraproject.org/pub/epel/6/
http://mirrors.163.com/centos/6/os/x86_64/
http://nginx.org/packages/centos/6/
2. 安装 php 的 redis 扩展
https://github.com/owlient/phpredis
cd phpredis-master
phpize
./configure --with-php-config=/usr/bin/php-config
make && make install
# vim /etc/php.ini #添加以下行
extension=redis.so #加载 redis 模块
date.timezone = Asia/Shanghai #设置时区
# sed -i 's/apache/nginx/g' /etc/php-fpm.d/www.conf
# service php-fpm start
3. 简单配置 nginx
# vim /etc/nginx/conf.d/default.conf
server {
listen
80;
server_name localhost;
#修改 php-fpm 用户location / {
root
/usr/share/nginx/html;
index index.php index.html index.htm;
}
location ~ \.php$ {
root
/usr/share/nginx/html;
fastcgi_pass
127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME
/usr/share/nginx/html/$fastcgi_script_name;
include
fastcgi_params;
}
}
# service nginx start
4. 配置 mysql
# service mysqld start
mysql> grant all on test.* to redis@localhost identified by 'westos';
Query OK, 0 rows affected (0.00 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
5. 创建 php 测试页面
<?php
$redis = new Redis();
$redis->connect('127.0.0.1',6379) or die ("could net connect redis
server");
$query = "select * from test limit 9";
for ($key = 1; $key < 10; $key++)
{
if (!$redis->get($key))
{
$connect = mysql_connect('127.0.0.1','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 "<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>";
}
?>
测试结果:
到这里,我们已经实现了 redis 作为 mysql 的缓存服务器,但是如果更新了 mysql,redis中仍然会有对应的 KEY,数据就不会更新,此时就会出现 mysql 和 redis 数据不一致的情况。所以接下来就要通过 mysql 触发器将改变的数据同步到 redis 中。
配置 gearman 实现数据同步
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 数据库的更新。
1. 安装 gearman 软件包:
gearmand libgearman-devel libgearman libevent libevent-devel
libevent-doc libevent-headers tokyocabinet
# service gearmand start
#启动服务
# netstat -antlp |grep gearmand
tcp
0
0 0.0.0.0:4730
7304/gearmand
0.0.0.0:*
LISTEN
2. 安装 php 的 gearman 扩展
https://pecl.php.net
yum install -y db*-devel
tar zxf gearman-1.1.2.tgz
cd gearman-1.1.2
./configure --with-php-config=/usr/bin/php-config
make && make install
# vim /etc/php.ini
extension=gearman.so
# service php-fpm reload
3. 安装 lib_mysqludf_json
lib_mysqludf_json UDF 库函数将关系数据映射为 JSON 格式。通常,数据库中的数据映射为 JSON 格式,是通过程序来转换的。
https://github.com/mysqludf/lib_mysqludf_json
# yum install -y mysql-devel# unzip lib_mysqludf_json-master.zip
# cd lib_mysqludf_json-master
# gcc $(mysql_config --cflags) -shared -fPIC -o lib_mysqludf_json.so
lib_mysqludf_json.c
查看 mysql 的模块目录:
mysql> show global variables like 'plugin_dir';
+---------------+-------------------------+
| Variable_name | Value
|
+---------------+-------------------------+
| plugin_dir
| /usr/lib64/mysql/plugin |
+---------------+-------------------------+
拷贝 lib_mysqludf_json.so 模块:
# cp lib_mysqludf_json.so /usr/lib64/mysql/plugin/
注册 UDF 函数
mysql> CREATE FUNCTION json_object RETURNS STRING SONAME
'lib_mysqludf_json.so';
查看函数
mysql> select * from mysql.func;
+--------------------+-----+-------------------------+----------+
| name
| ret | dl
| type
|
+--------------------+-----+-------------------------+----------+
| json_object
| 0 | lib_mysqludf_json.so | function |
+--------------------+-----+-------------------------+----------+
4. 安装 gearman-mysql-udf
这个插件是用来管理调用 Gearman 的分布式的队列。
https://launchpad.net/gearman-mysql-udf
# tar zxf gearman-mysql-udf-0.6.tar.gz
# cd gearman-mysql-udf-0.6
# ./configure --with-mysql=/usr/bin/mysql_config
--libdir=/usr/lib64/mysql/plugin/
# make# make install
注册 UDF 函数
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';
查看函数
mysql> 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 |
+--------------------+-----+-------------------------+----------+
指定 gearman 的服务信息
mysql> SELECT gman_servers_set('127.0.0.1:4730');
+------------------------------------+
| gman_servers_set('127.0.0.1:4730') |
+------------------------------------+
| 127.0.0.1:4730
|
+------------------------------------+
5. 编写 mysql 触发器(根据实际情况编写)
# vim test.sql
use test;
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 < test.sql
查看触发器
mysql> SHOW TRIGGERS FROM test;+-------------+--------+-------+------------------------------------------------------------------------
-------+----------------------+--------------------+
| datatoredis | UPDATE | test | BEGIN
SET @RECV=gman_do_background('syncToRedis', json_object(NEW.id as
`id`, NEW.name as `name`));
END | AFTER | NULL
|
| root@localhost | latin1
|
latin1_swedish_ci
| latin1_swedish_ci |
+-------------+--------+-------+------------------------------------------------------------------------
-------+----------------------+--------------------+
6. 编写 gearman 的 worker 端
# vim worker.php
<?php
$worker = new GearmanWorker();
$worker->addServer();
$worker->addFunction('syncToRedis', 'syncToRedis');
$redis = new Redis();
$redis->connect('127.0.0.1', 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); #这条语句就是将 id 作 KEY 和
name 作 VALUE 分开存储,需要和前面写的 php 测试代码的存取一致。
}
?>
后台运行 worker
# nohup php worker.php &
7. 更新 mysql 中的数据
mysql> update test set name='hello' where id=1;查看 redis
# redis-cli
127.0.0.1:6379> get 1
"hello"
刷新测试页面数据同步