Linux下用Redis作爲緩存服務器,加快數據庫操作速度
一.前言
對一個關係型數據庫進行調優以獲得高查詢性能可能會比較困難。如果對數據模型優化和對查詢調優不起作用,DBA就可以使用緩存系統,比如Redis,它是一個可以提供內存和永久數據存儲的鍵值數據存儲系統。
由於Redis能夠將數據快速讀寫至數據存儲系統,比起關係型數據庫它更具性能優勢。但是鍵值數據存儲比較簡單,它們沒有類似SQL那樣的查詢語言或是結構化數據模型。取而代之的是,它們包含用鍵作爲標識符並與值相關聯的一個簡單字典或是哈希模型。DBA可以通過這些鍵來存儲和檢索值。
鍵值存儲簡單而又快速,這使得它們可以很好地匹配關係型數據庫豐富的數據模型和查詢功能。有時使用鍵值和關係型數據庫的組合是更好的選擇。另外,還有大量支持商業化的鍵值數據庫,包括Redis,Riak和Areospike。
要運行Redis緩存來優化常用查詢的性能,首先要對你想要緩存的查詢結果進行識別。將注意力集中在那些最頻繁使用和耗時的查詢上,然後從你要緩存的查詢中識別數據。簡言之,就是緩存一個查詢返回的所有字段值。
該集羣中數據的流程爲:
client——>app——>redis——>mysql——>redis——>client
二.實驗環境(rhel7.3版本)
1.selinux和firewalld狀態爲disabled
2.各主機信息如下:
主機 | ip |
---|---|
server2(redis服務器) | 172.25.63.2 |
server3(mysql數據庫) | 172.25.63.3 |
server4(nginx服務器) | 172.25.63.4 |
三.實驗一
配置server1
因爲server1之前是做過redis cluster集羣的。所以要將之前打開的redis-server的進程殺掉
前期準備:
殺掉redis-server的進程
(1)安裝相應的軟件,以提供killall命令(killall工具可以殺死所有關鍵字進程)
[root@server4 7008]# yum install psmisc-22.20-11.el7.x86_64 -y
(2)殺掉redis-server的所有進程
[root@server4 7008]# killall redis-server
此時再次查看進程,發現已經不存在redis-server的進程了
1.配置nginx服務
源碼編譯nginx服務
(1)下載nginx對應的壓縮包:
[root@server4 ~]# tar zxf nginx-1.16.1.tar.gz
[root@server4 ~]# cd nginx-1.16.1
(2)安裝nginx所需的依賴包
[root@server4 nginx-1.16.1]# yum install gcc pcre-devel zlib-devel -y
(4)源碼編譯並安裝nginx:
[root@server4 nginx-1.16.1]# ./configure --prefix=/usr/local/nginx #預編譯
[root@server4 nginx-1.16.1]# make && make install
(5)修改nginx服務對應的配置文件,以使得nginx服務支持php語言
[root@server4 nginx-1.16.1]# cd /usr/local/nginx/
[root@server4 nginx]# ln -s /usr/local/nginx/sbin/nginx /usr/local/sbin/
[root@server4 nginx]# cd conf/
[root@server4 conf]# useradd -u 900 nginx
[root@server4 conf]# vim nginx.conf
2 user nginx nginx;
65 location ~ \.php$ {
66 root html;
67 fastcgi_pass 127.0.0.1:9000;
68 fastcgi_index index.php;
69 #fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; #將69行註釋
70 include fastcgi.conf; #將70行該爲fastcgi.conf
71 }
43 location / {
44 root html;
45 index index.php index.html index.htm; #在45行的前面添加index.php
46 }
(6)檢測nginx服務
[root@server4 conf]# nginx -t
(7)啓動nginx服務
[root@server4 conf]# nginx
在瀏覽器中輸入172.25.63.4進行測試,以確保nginx服務已經搭建好
2.配置php
下載php對應的安裝包及其對應的依賴包,並進行安裝,以使得nginx服務支持php語言。
[root@server4 rhel7]# yum install * -y
啓動 php-fpm
[root@server4 rhel7]# systemctl start php-fpm
查看9000端口確認開啓成功
[root@server4 rhel7]# netstat -antlupe | grep 9000
tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN 0 68512 21269/php-fpm: mast
3.編寫redis服務對應的測試文件
編寫redis服務對應的測試文件(test.php文件),並將該文件拷貝到nginx服務的默認發佈目錄中,命名爲index.php並進行測試,修改index.php文件,使得該服務指向server2的redis服務,指向server3的mysql數據庫
#將第3行的ip改爲server2的ip
#將第10行的ip改爲server3的ip
#值的注意的是第10行的redis是mysql數據庫需要創建的用戶,redhat是redis這個用戶對應的密碼
[root@server4 html]# ls
50x.html index.html test.php
[root@server4 html]# mv test.php index.php
[root@server4 html]# vim index.php
<?php
$redis = new Redis();
$redis->connect('172.25.63.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.63.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 "<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
在 server2 上配置 redis 爲 master,因爲之前做了主從,關閉 server4 的 redis
[root@server2 ~]# vim /etc/redis/6379.conf #需要將之前在最後一行寫的slaveof 172.25.63.4 6379這行刪掉;
70 bind 0.0.0.0 #第70行的監聽所有端口的內容不用動
刪除原來的 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
開啓 server3,關閉原來的 mysql,若原來沒有mysql則直接安裝mariadb
[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)
爲 server3 上的 mysql 的 test 庫加入一些數據
[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 -predhat < test.sql
測試一:
在瀏覽器中輸入172.25.63.4進行測試
可以看到導入的數據
但是刷新一次後,可以看到後面就從 redis 讀取數據
使用命令行也可以看到:
[root@server2 ~]# redis-cli
127.0.0.1:6379> get 1
"test1"
127.0.0.1:6379> get 2
"test2"
127.0.0.1:6379>
測試二:
爲了查看瀏覽器,看到的數據是隨着redis服務中的數據的設置而改變,還是隨着數據庫中的數據的改變而改變
1.在server3登陸數據庫,修改數據(將id爲1對應的數據改爲westos)
[root@server3 ~]# mysql -uroot -proot
MariaDB [(none)]> use test;
MariaDB [test]> update test set name='westos' where id=1;
2.在server2查看數據
[root@server2 ~]# redis-cli
127.0.0.1:6379> get 1
"test1"
我們可以看到id爲1對應的數據,仍然是test1,而並不是數據庫中顯示的westos。
3.在瀏覽器中輸入172.25.63.4進行查看數據
結論:
瀏覽器,看到的數據不是隨着數據庫中的數據的改變而改變。
到這裏,我們已經實現了redis作爲mysql的緩存服務器,但是如果更新了mysql,redis中仍然會有對應的key,數據就不會更新。
此時,就會出現mysql和redis數據不一致的情況。所以接下來就要通過mysql觸發器將改變的數據同步到redis中
四.實現數據庫更新,redis的數據也更新
1.Gearman+PHP+MySQL UDF的組合異步實現MySQL到Redis的數據複製。
用redis作爲Mysql數據庫的緩存,在查找的時候,首先查找redis緩存,如果找到則返回結果;如果在redis中沒有找到,那麼查找Mysql數據庫,找到的花則返回結果並且更新redis;如果沒有找到則返回空。對於寫入的情況,直接寫入mysql數據庫,mysql數據庫通過觸發器及UDF機制自動把變更的內容更新到redis中。採用MySQL作爲數據存儲引擎,Redis則作爲Cache。
mysql讀寫數據都需要從磁盤讀取 。磁盤的容量,帶寬的大小就影響了網站的訪問速度,讀取的方式,也就是sql語句,次數和效率也會影響讀取效率。
redis和mc都是緩存,並且都是駐留在內存中運行的,這大大提升了高數據量web訪問的訪問速度。然而mc只是提供了簡單的數據結構,比如 string存儲;redis卻提供了大量的數據結構,比如string、list、set、hashset、sorted set這些,這使得用戶方便了好多,畢竟封裝了一層實用的功能,同時實現了同樣的效果,當然用redis而慢慢捨棄mc。
2.這個實驗的實現需要使用新的工具gearmand
Gearman是一套用來把程式需求委派給機器,提供通用的程序框架來將任務分發在機器運算。它同時具備並行工作的能力、負載均衡處理的能力,以及在不同程序語言之間溝通的能力。
運行過程:
一個Gearman請求的處理過程涉及三個角色:Client -> Job -> Worker。
Client:請求的發起者,可以是 C,PHP,Perl,MySQL UDF 等等。
Job:請求的調度者,用來負責協調把 Client 發出的請求轉發給合適的 Worker。
Worker:請求的處理者,可以是 C,PHP,Perl 等等。
因爲 Client,Worker 並不限制用一樣的語言,所以有利於多語言多系統之間的集成。
甚至我們通過增加更多的 Worker,可以很方便的實現應用程序的分佈式負載均衡架構。
大致流程:
下面要編寫的 mysql 觸發器,就相當於 Gearman 的客戶端。修改表,插入表就相當於直接下發任務。然後通過lib_mysqludf_json UDF 庫函數將關係數據映射爲 JSON 格式,然後再通過 gearman-mysql-udf 插件將任務加入到 Gearman 的任務隊列中,最後通過redis_worker.php,也就是 Gearman 的 worker 端來完成 redis 數據庫的更新。
實際的工作流程:mysql(client)——>gearmand:4730(job server)——>worker(php/python/java)
配置server3:
1.下載lib_mysqludf_json的使用過程中,依賴的軟件
[root@server3 ~]# yum install gcc -y
[root@server3 ~]# yum install mariadb-devel -y #因爲數據庫安裝的是mariadb,所以這裏安裝的包是mariadb-devel,而不是,mysql-devel
2.安裝 lib_mysqludf_json
使用lib_mysqludf_json的原因是因爲Gearman只接受字符串作爲入口參數,可以通過lib_mysqludf_json將MySQL中的數據編碼爲JSON字符串。
<1>下載lib_mysqludf_json對應的壓縮包:lib_mysqludf_json-master.zip,並進行安裝
[root@server3 ~]# yum install unzip -y
[root@server3 ~]# unzip lib_mysqludf_json-master.zip
[root@server3 ~]# cd lib_mysqludf_json-master
[root@server3 lib_mysqludf_json-master]# gcc $(mysql_config --cflags) -shared -fPIC -o lib_mysqludf_json.so lib_mysqludf_json.c
可以看到重新編譯生成了 lib_mysqludf_json.so 文件
<2>查看 mysql 的模塊目錄:
[root@server3 lib_mysqludf_json-master]# mysql -uroot -proot
MariaDB [(none)]> show global variables like 'plugin_dir';
<3>拷貝 lib_mysqludf_json.so 模塊到mysql的模塊目錄
[root@server3 lib_mysqludf_json-master]# cp lib_mysqludf_json.so /usr/lib64/mysql/plugin/
<3>註冊 UDF 函數
[root@server3 lib_mysqludf_json-master]# mysql -uroot -predhat
MariaDB [(none)]> CREATE FUNCTION json_object RETURNS STRING SONAME 'lib_mysqludf_json.so';
<4>查看函數
[root@server3 lib_mysqludf_json-master]# mysql -uroot -proot
MariaDB [(none)]> select * from mysql.func;
3.安裝 gearman-mysql-udf
這個插件是用來管理調用 Gearman 的分佈式的隊列。
<1>下載gearman-mysql-udf對應的軟件包:gearman-mysql-udf-0.6.tar.gz,及其依賴包(libgearman-1.1.12-18.el7.x86_64.rpm,libgearman-devel-1.1.12-18.el7.x86_64.rpm和libevent-devel-2.0.21-4.el7.x86_64.rpm)。並進行編譯安裝
[root@server3 ~]# 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 #安裝依賴包
[root@server3 ~]# tar zxf gearman-mysql-udf-0.6.tar.gz #解壓gearman-mysql-udf的壓縮包
[root@server3 ~]# cd gearman-mysql-udf-0.6
[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 #編譯與安裝
<2>註冊 UDF 函數
[root@server3 ~]# mysql -uroot -predhat
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';
<3>查看函數
[root@server3 ~]# mysql -uroot -proot
MariaDB [(none)]> select * from mysql.func;
<4>指定 gearman 的服務信息
[root@server3 ~]# mysql -uroot -predhhat
MariaDB [(none)]> SELECT gman_servers_set('172.25.63.4:4730');
4. 編寫 mysql 觸發器(根據實際情況編寫)
也就是家目錄下面的test.sql文件
test.sql文件的內容:(這裏只用到1,5-9行;2-3行是上個實驗需要的內容,所以這裏將其註釋掉)
<1>將test.sql文件導入到test數據庫中
[root@server3 ~]# mysql -predhat < test.sql
<2>查看觸發器
[root@server3 ~]# mysql -uroot -proot
MariaDB [(none)]> SHOW TRIGGERS FROM test;
配置server4
1.安裝 php 的 gearman 擴展(前面已經安裝過),啓動gearmand服務(對應的端口是470端口)
[root@server1 ~]# systemctl start gearmand
[root@server1 ~]# netstat -antulpe | grep 4730
2.查看php的支持,看php是否支持redis,mysql和gearman
[root@server1 ~]# php -m
gearman
mysql
redis
3.編寫 gearman 的 worker 端——worker.php文件
worker.php文件的內容如下:(其中第7行的ip地址指向server2——redis服務器)
[root@server4 local]# cat worker.php
<?php
$worker = new GearmanWorker();
$worker->addServer();
$worker->addFunction('syncToRedis', 'syncToRedis');
$redis = new Redis();
$redis->connect('172.25.63.2', 6379); #redis服務器
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);
}
?>
測試:
1.在server4端:後臺運行 worker
[root@server4 ~]# nohup php /usr/local/worker.php &> /dev/null &
[1] 2087
2.在server3端:更新 mysql 中的數據
[root@server3 ~]# mysql -uroot -predhat
MariaDB [(none)]> use test;
MariaDB [test]> update test set name='redhat' where id=2;
3.在server2端:查看 redis
[root@server2 ~]# redis-cli
127.0.0.1:6379> get 2
"redhat"
4.在瀏覽器端:刷新測試頁面看數據數據同步
結論:
mysql數據庫端的數據的修改,影響了redis端的數據,也影響了瀏覽器端的數據。即mysql數據庫,redis端和nginx端的數據實現了同步。表示實驗成功。