实验环境:
server1 172.25.254.1 做nginx,用户访问的入口
server2 172.25.254.2 做redis,缓存数据
server3 172.25.254.3 做mysql,真实存储数据
原理:
用 redis 用来缓存热点数据,来降低mysql的访问压力,80%的访问都集中在20%的数据上,所以我们把这20%的数据放到 redis 中。
访问流程:
client --> app(tomcat..) --> redis -> mysql -> redis -> client
如果redis中没有,就先取mysql中缓存到redis中,在从redis中读取,返回给客户端
redis从Mysql中缓存
server1中:
我们编译安装nginx-1.18,
vim auto/cc/gcc
在里面注释掉dubug,使安装更小更快
yum install -y pcre-devel 安装依赖性
yum install zlib-devel -y
./configure --prefix=/usr/local/nginx 预编译
make && make install
配置一下:nginx.conf
打开php功能模块.
/usr/local/nginx/sbin/nginx 启动
测试访问:
配置一个php页面,
但是我们当前不能解析php,我们需要去配置php的一些插件:
yum install 安装。
启动php,9000端口和80端口打开
测试访问php页面:
访问成功,更改index.php文件内容为:
<?php
$redis = new Redis();
$redis->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.3','redis','caoaoyuan');
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>";
}
?>
server2中:
配置redis,并启动。
server3中:
安装mariadb,并启动。
yum install -y mariadb-server
systemctl start mariadb.service
登陆数据库,
MariaDB [(none)]> create database test;
Query OK, 1 row affected (0.00 sec)
#创建一个test的表,因为我们server1上index.php 访问的就是test的表,可去上面查看
MariaDB [(none)]> grant all on test.* to redis@'%' identified by 'caoaoyuan';
Query OK, 0 rows affected (0.00 sec)
#授权用户
MariaDB [(none)]> flush privileges;
Query OK, 0 rows affected (0.00 sec)
#刷新授权表
测试
我们给数据库导入一些数据进行测试
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 -pcaoaoyuan < test.sql 导入
访问:
代表这些数据时从mysql获取的,我们在刷新一次:
就说明这些数据时从redis获取的,说明第一次将数据存储到了redis,第二次直接从redis中读取。
而且:
我们的redis刚才是没有数据的,这时可以获取到缓存的数据了。
如果Mysql中的数据进行了变更,这时我们redis的数据会不会同时更新那?
MariaDB [test]> update test set name='westos' where id=1;
Query OK, 1 row affected (0.04 sec)
Rows matched: 1 Changed: 1 Warnings: 0
更新数据
访问:
没有更新。除非我们手动更新。
redis 同步mysql
所以我们应定时同步mysql的数据。这时我们用到了gearmand服务。使用udf的方式。
在server1中:
我们在server3数据库中配置,当数据更新时会触发触发器,触发器会触发mysql中的 udf(user defined function) 插件,这个擦件会调用gearman的服务。它会调用 worker 再通过worker 调用php-pecl-redis,从而把数据同步到redis中。
一个由Gearman驱动的应用程序由三部分组成:一个客户端、一个worker和一个job服务器。客户端负责创建要运行的作业并将其发送到作业服务器。作业服务器将找到一个合适的工作者,可以运行作业并转发作业。worker执行客户机请求的工作,并通过作业服务器向客户机发送响应。Gearman提供了客户端和工作者api,您的应用程序可以调用这些api与Gearman作业服务器(也称为gearmand)进行对话,因此您不需要处理作业的联网或映射。在内部,gearman客户端和工作api使用TCP套接字与作业服务器进行通信。
我们的server1就相当于job服务器,用来分发任务,在server3充当客户端。
启动服务,会开启一个4730的端口
server3中:
安装插件,
yum install mariadb-devel.x86_64 -y 否则不支持 udf
编译这个用户自定义函数:
gcc $(mysql_config --cflags) -shared -fPIC -o lib_mysqludf_json.so lib_mysqludf_json.c
cp lib_mysqludf_json.so /usr/lib64/mysql/plugin/
把它放到数据库的默认插件目录中去。
现在我们注册刚才编译的 udf函数,我们创建一个json对象,把数据全部映射为json格式,因为json格式可读性比较强。
MariaDB [(none)]> CREATE FUNCTION json_object RETURNS STRING SONAME 'lib_mysqludf_json.so';
Query OK, 0 rows affected (0.00 sec)
yum install 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 -y
安装一些gearman的库文件和依赖性
编译安装gearman-mysql-udf插件:
./configure --libdir=/usr/lib64/mysql/plugin --with-mysql
make && make install
这样我们就在server3中安装好了需要的插件。
注册udf函数的插件。
CREATE FUNCTION gman_do_background RETURNS STRING SONAME 'libgearman_mysql_udf.so';
CREATE FUNCTION gman_servers_set RETURNS STRING SONAME 'libgearman_mysql_udf.so';
总体就是这三个函数。
查找服务:
然后我们需要去创建触发器。
与上次不同。
导入到mysql;
[root@server3 ~]# mysql -pcaoaoyuan < test.sql
这就是我们刚定义的触发器。
现在我们在server1中去配置worker端,谁去执行这个同步的任务。
vim worker.php
<?php
$worker = new GearmanWorker();
$worker->addServer();
$worker->addFunction('syncToRedis', 'syncToRedis');
$redis = new Redis();
$redis->connect('172.25.254.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 rhel7]# cp worker.php /usr/local/
我们可以直接用php执行,这个进程会一直运行worker.php,会一直从redis中同步数据
[root@server1 rhel7]# nohup php /usr/local/worker.php &> /dev/null &
[1] 9080
测试:
在server3中:
# 更新数据
MariaDB [test]> update test set name='westos' where id=2;
Query OK, 1 row affected (0.29 sec)
Rows matched: 1 Changed: 1 Warnings: 0
# 查看
MariaDB [test]> select * from test;
+----+--------+
| id | name |
+----+--------+
| 1 | westos |
| 2 | westos |
| 3 | test3 |
| 4 | test4 |
| 5 | test5 |
| 6 | test6 |
| 7 | test7 |
| 8 | test8 |
| 9 | test9 |
+----+--------+
然后我们测试访问:
就同步成功了。1不会更新,开启gearman后 2就更新了
- 原理
- mysql的变更触发 trigger ,然后 json_map插件,将变更格式化为json格式,
- 然后找到 mysql 的 gearman 和 udf 的插件 ,然后找到 gearman(job-server / server1)
- 然后 jojb-server 将任务分发给 worker。
- worker 就和 php-gearman 和 php-redis 通信,将数据同步到server2上的 redis,redis 再发送到客户端,同时同步到本地缓存。