一、前言
1.读写分离的背景
实际的生产环境当中,客户端对数据库的读操作都是直接找redis拿数据的
如果redis缓存里面没有数据,那么就会去找mysql拿数据,并且给redis中缓存一份
redis中的数据有两种情况不能使用:数据过期了或者mysql中的数据更新了
用户读的时候访问redis,用户写的时候访问mysql
实际上读的需求量是很大的,redis刚好是把数据缓存在内存当中,响应速度也快
也可以降低我们后台mysql数据库的压力
2、为什么要有读写分离
一般对于高并发的系统来说,搭建一个健壮的缓存系统是不可避免的。
单机的reids的QPS可能只能上万,如果有再高并发的场景,单机是不能搞定的,就会有它的系统瓶颈。
一般来说缓存是用来支撑高并发读,这时候我们可能就会想到读写分离;
读写分离是用来处理读的并发量大,而写的并发量小的场景。
二、读写分离的实现
redis配合mysql
数据访问流程:client -> app -> redis -> mysql -> redis -> client
客户端用app访问,先在redis里读数据,因为快,redis没有才去mysql读,读完保存在redis里,然后返回客户端,下次再读就快
1、实验准备:
主机名 | IP | 功能 |
server1 | 172.25.58.1 | 前端服务器 |
server2 | 172.25.58.2 | redis服务器 |
server3 | 172.25.58.3 | mysql服务器 |
先停止server1和server3上的redis服务
[root@server1 ~]# killall redis-server #由于之前有创建redis集群
#没有killall命令 yum whatprovides /usr/bin/killall
#[root@server1 ~]# yum install -y psmisc-22.20-11.el7.x86_64
或者:
[root@server3 ~]# /etc/init.d/redis_6379 stop
[root@server3 ~]# chkconfig redis_6379 off
开启server2上面的redis服务,并且查看redis节点是否为master节点,并且没有slave节点
搭建好共享yum源
2、基本读写分离的具体过程如下
server1上:
(1)、先安装nginx:具体详见我的关于nginx部署的博客https://mp.csdn.net/postedit/102691858
源码安装完nginx
[root@server1 ~]# ls
nginx-1.14.0.tar.gz redis-5.0.3 redis-5.0.3.tar.gz
[root@server1 ~]# tar -xzvf nginx-1.14.0.tar.gz
[root@server1 ~]# cd nginx-1.14.0
[root@server1 nginx-1.14.0]# yum install openssl-devel -y
[root@server1 nginx-1.14.0]# yum install prce-devel -y
[root@server1 nginx-1.14.0]# ./configure --prefix=/usr/local/nginx --with-http_ssl_module --with-http_stub_status_module --with-threads --with-file-aio --user=nginx --group=nginx
[root@server1 nginx-1.14.0]# make && make install
(2)、编辑nginx的配置文件,将nginx的用户改为nginx,并加入php发布文件
[root@server1 nginx]# groupadd -g 900 nginx
[root@server1 nginx]# useradd -u 900 -g 900 nginx
[root@server1 nginx]# vim /usr/local/nginx/conf/nginx.conf
2 #user nobody;
3 user nginx 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;
}
(2)、启动
启动并测试nginx
[root@server1 nginx]# /usr/local/nginx/sbin/nginx
nginx: [emerg] invalid number of arguments in "user" directive in /usr/local/nginx/conf/nginx.conf:4
###启动php-fpm
[root@server1 ~]# systemctl start php-fpm
##在server2上配置redis为master,因为之前做了主从,关闭server1的redis
#删除原来的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)
##配置测试页面
[root@server1 ~]# mv test.php /usr/local/nginx/html/
[root@server1 html]# mv test.php index.php
##再开启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)
##修改server1上nginx的发布文件test.php
vim test.php
3 $redis->connect('172.25.0.2',6379) or die ("could net connect r edis server");
10 $connect = mysql_connect('172.25.0.3','redis',' redhat');
##为server3上的mysql的test库加入一些数据
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
#浏览器访问测试
172.25.0.1
可以看到导入的数据
mysql
number is 1
name is
number is 2
name is
number is 3
name is
number is 4
name is
number is 5
name is
number is 6
name is
number is 7
name is
number is 8
name is
number is 9
name is
#但是刷新一次后,可以看到后面就从redis读取数据
redis
number is 1
name is test1
number is 2
name is test2
number is 3
name is test3
number is 4
name is test4
number is 5
name is test5
number is 6
name is test6
number is 7
name is test7
number is 8
name is test8
number is 9
name is test9
#使用命令行也可以看到
[root@server2 ~]# redis-cli
127.0.0.1:6379> get 1
"test1"
127.0.0.1:6379> get 2
"test2"
##这个时候有个问题,如果此时mysql数据发生变更,redis会同步吗?
#在server3上
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
#变更后发现,redis里没有变,浏览器也看不到变化,但是在redis里做的变化能看到
127.0.0.1:6379> set 2 redhat
OK
#刷新浏览器,看到变化,这样显然不合理
######配置gearmand######
在server1上启动
[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';
#安装插件管理gearman的分布式队列
[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.0.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 ~]# cp worker.php /usr/local/
[root@server1 local]# vim worker.php
7 $redis->connect('172.25.0.2', 6379);
##后台运行worker
[root@server1 ~]# nohup php /usr/local/worker.php &> /dev/null &
##修改数据库内容
MariaDB [test]> update test set name='westos' where id=2;
##在redis上查看
127.0.0.1:6379> get 2
"westos"
##页面查看,数据同步
redis
number is 1
name is redhat
number is 2
name is westos
number is 3
name is test3
number is 4
name is test4
number is 5
name is test5
number is 6
name is test6
number is 7
name is test7
number is 8
name is test8
number is 9
name is test9