實驗環境:
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 再發送到客戶端,同時同步到本地緩存。