一、redis簡介
Redis是一個key-value儲存系統。和Memcached類似,為了保證效率,資料都是快取在記憶體中。區別的是redis會週期性的把更新的資料寫入磁碟或者把修改操作寫入追加的記錄檔案,並且在此基礎上實現了master-slave(主從)同步。在部分場合可以對關聯式資料庫起到很好的補充作用。它提供了Java,C/C++(hiredis),C#,PHP,JavaScript,Perl,Object-C,Python,Ruby等客戶端,使用很方便。
二、架構圖
大致結構就是讀寫分離,將mysql中的資料通過觸發器同步到redis中
三、安裝LNMP環境(這裡為了省事,就是用yum來安裝)
1、修改yum源
1
2
3
4
5
6
7
8
9
10
11
12
13
|
[iyunv@redis ~]# vim /etc/yum.repos.d/epel.repo #新增這個檔案
[epel]
name=Extra Packages for Enterprise Linux 6 – $basearch
baseurl=http://download.fedoraproject.org/pub/epel/6/$basearch
failovermethod=priority
enabled=1
gpgcheck=0
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/6/$basearch/
gpgcheck=0
enabled=1
|
2、yum安裝
1
|
[iyunv@redis ~]# yum -y install nginx php php-fpm php-cli php-common php-gd php-mbstring php-mysql php-pdo php-devel php-xmlrpc php-xml php-bcmath php-dba php-enchant mysql mysql-server
|
3、簡單配置一下nginx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
[iyunv@redis ~]# vim /etc/nginx/nginx.conf
server {
listen 80;
#定義使用www.xx.com訪問
server_name www.xx.com;
#設定本虛擬主機的訪問日誌
access_log /logs/www.xx.com.access.log main;
#預設請求
location / {
root /www/; #定義伺服器的預設網站根目錄位置
index index.php index.html index.htm; #定義首頁索引檔案的名稱
}
location ~ .php$ {
root /www/;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /www/$fastcgi_script_name;
include fastcgi_params;
}
}
|
4、啟動服務
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
[iyunv@redis ~]# sed -i `s/apache/nginx/g` /etc/php-fpm.d/www.conf
[iyunv@redis ~]# /etc/init.d/php-fpm start
正在啟動 php-fpm: [確定]
[iyunv@redis ~]# /etc/init.d/mysqld start
正在啟動 mysqld: [確定]
[iyunv@redis ~]# mkdir /{logs,www}
[iyunv@redis ~]# chown -R nginx:nginx /{logs,www}
[iyunv@redis ~]# /etc/init.d/nginx start
正在啟動 nginx: [確定]
[iyunv@redis www]# service iptables stop
iptables: Flushing firewall rules: [ OK ]
iptables: Setting chains to policy ACCEPT: filter [ OK ]
iptables: Unloading modules: [ OK ]
[iyunv@redis redis]# netstat -tnlp #檢視監聽
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 2101/nginx
tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN 7544/php-fpm
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN 1871/mysqld
|
5、給mysql授權
1
2
3
4
|
[iyunv@redis ~]# mysql
mysql> grant all privileges on *.* to root@localhost identified by `123456`;
mysql> flush privileges;
|
6、測試
1
2
3
4
|
[iyunv@redis ~]# vim /www/index.php
<?php
phpinfo();
?>
|
然後訪問頁面看到php的相關資訊,基礎環境就算搭建完成了。
四、安裝redis
1、安裝redis
1
2
3
4
5
6
7
8
9
10
11
12
|
[iyunv@redis ~]# wget -c -t 0 http://download.redis.io/releases/redis-2.8.19.tar.gz
[iyunv@redis ~]# mkdir /usr/local/redis
[iyunv@redis ~]# tar xvf redis-2.8.19.tar.gz
#安裝很簡單、直接make就可以了
[iyunv@redis ~]# cd redis-2.8.19
[iyunv@redis redis-2.8.19]# make
#編譯完成後,將src中的可執行檔案拷貝到剛剛建立的目錄中
[iyunv@redis src]# cp redis-benchmark redis-check-aof redis-check-dump redis-cli redis-sentinel redis-server /usr/local/redis/
[iyunv@redis redis-2.8.19]# cp redis.conf sentinel.conf /usr/local/redis/
|
Redis-benchmark 壓力測試工具
Redis-check-aof 檢查redis持久化命令檔案的完整性
Redis-check-dump 檢查redis持久化資料檔案的完整性
Redis-cli redis在linux上的客戶端
Redis-sentinel redis-sentinel是叢集管理工具,主要負責主從切換。
Redis-server Redis伺服器的daemon啟動程式
2、安裝php的redis擴充套件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
[iyunv@redis ~]# wget -c -t 0 https://github.com/owlient/phpredis/archive/master.zip
[iyunv@redis ~]# unzip master.zip
[iyunv@redis ~]# cd phpredis-master/
[iyunv@redis phpredis-master]# phpize
[iyunv@redis phpredis-master]# ./configure –with-php-config=/usr/bin/php-config
[iyunv@redis phpredis-master]# make && make install
#修改php的配置檔案,如果沒有“extension=redis.so”,就加上這一行
[iyunv@redis ~]# vim /etc/php.ini
extension=redis.so
[iyunv@redis ~]# /etc/init.d/php-fpm restart
停止 php-fpm: [確定]
正在啟動 php-fpm: [確定]
|
3、是否安裝成功
還是訪問phpinfo的那個介面
看到這個就是安裝完成了。
五、讀寫分離 這裡只是簡單的做了一下讀,沒有寫操作的相關程式碼,過一會測試,直接到資料庫裡執行update來模擬寫操作。
1、在mysql中插入一些測試資料
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
[iyunv@redis ~]# mysql -u root -p123456
mysql> create database mytest;
mysql> CREATE TABLE `test` (`id` int(7) NOT NULL AUTO_INCREMENT, `name` char(8) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
mysql> INSERT INTO `test` VALUES (1,`sven`),(2,`jim`),(3,`zhu`),(4,`wang`),(5,`ftd`),(6,`test`),(7,`test01`),(8,`test02`),(9,`test03`);
mysql> select * from mytest.test;
+—-+——–+
| id | name |
+—-+——–+
| 1 | sven |
| 2 | jim |
| 3 | zhu |
| 4 | wang |
| 5 | ftd |
| 6 | test |
| 7 | test01 |
| 8 | test02 |
| 9 | test03 |
+—-+——–+
|
2、編寫php的測試程式碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
<?php
$redis = new Redis();
$redis->connect(`127.0.0.1`,6379) or die (“could net connect redis server”);
$query = “select * from test limit 8”;
//為了簡單一點,這裡就讀取了8條資料
for ($key = 1; $key < 9; $key++)
{
if (!$redis->get($key))
{
$connect = mysql_connect(`127.0.0.1`,`root`,`123456`);
mysql_select_db(mytest);
$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 < 9; $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中沒有對應的KEY時
再次訪問,此時redis中已有相關資料
到這裡,我們已經實現了redis作為mysql的快取伺服器,但是如果更新了mysql,redis中仍然會有對應的KEY,資料就不會更新,此時就會出現mysql和redis資料不一致的情況。所以接下來就要通過mysql觸發器將改變的資料同步到redis中。
六、通過gearman實現同步
1、介紹
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資料庫的更新。
2、安裝啟動
1
2
3
4
5
|
[iyunv@redis ~]# yum -y install gearmand libgearman-devel
[iyunv@redis ~]# /etc/init.d/gearmand start
正在啟動 gearmand: [確定]
[iyunv@redis ~]# /etc/init.d/gearmand status
gearmand (pid 7702) 正在執行…
|
3、安裝php的gearman擴充套件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
[iyunv@redis ~]# wget -c -t 0 https://pecl.php.net/get/gearman-1.1.1.tgz
[iyunv@redis ~]# tar xvf gearman-1.1.1.tgz
[iyunv@redis ~]# cd gearman-1.1.1
[iyunv@redis gearman-1.1.1]# phpize
[iyunv@redis gearman-1.1.1]# ./configure –with-php-config=/usr/bin/php-config
[iyunv@redis gearman-1.1.1]# make
[iyunv@redis gearman-1.1.1]# make install
#如果php的配置檔案中沒有extension = gearman.so,就加上此行
[iyunv@redis ~]# vim /etc/php.ini
extension = gearman.so
[iyunv@redis ~]# /etc/init.d/php-fpm restart
停止 php-fpm: [確定]
正在啟動 php-fpm: [確定]
|
這樣就是安裝成功了
4、安裝lib_mysqludf_json
lib_mysqludf_json UDF庫函式將關係資料對映為JSON格式。通常,資料庫中的資料對映為JSON格式,是通過程式來轉換的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
[iyunv@redis ~]# wget -c -t 0 https://github.com/mysqludf/lib_mysqludf_json/archive/master.zip
[iyunv@redis ~]# unzip master.zip
[iyunv@redis ~]# cd lib_mysqludf_json-master/
[iyunv@redis lib_mysqludf_json-master]# gcc $(mysql_config –cflags) -shared -fPIC -o lib_mysqludf_json.so lib_mysqludf_json.c
lib_mysqludf_json.c:40:23: 錯誤:my_global.h:沒有那個檔案或目錄
lib_mysqludf_json.c:41:20: 錯誤:my_sys.h:沒有那個檔案或目錄
lib_mysqludf_json.c:43:19: 錯誤:mysql.h:沒有那個檔案或目錄
lib_mysqludf_json.c:44:21: 錯誤:m_ctype.h:沒有那個檔案或目錄
lib_mysqludf_json.c:45:22: 錯誤:m_string.h:沒有那個檔案或目錄
#這裡編譯報錯是因為沒有安裝mysql的開發包,如果是原始碼安裝的mysql,需要在/etc/ld.so.conf.d/
#目錄下新建一個檔案告訴系統mysql的標頭檔案在哪裡
[iyunv@redis lib_mysqludf_json-master]# yum -y install mysql-devel
[iyunv@redis lib_mysqludf_json-master]# gcc $(mysql_config –cflags) -shared -fPIC -o lib_mysqludf_json.so lib_mysqludf_json.c
mysql> show global variables like `plugin_dir`;
+—————+————————-+
| Variable_name | Value |
+—————+————————-+
| plugin_dir | /usr/lib64/mysql/plugin |
+—————+————————-+
#將模組拷貝到外掛目錄下
[iyunv@redis lib_mysqludf_json-master]# cp lib_mysqludf_json.so /usr/lib64/mysql/plugin/
#註冊UDF函式
mysql> CREATE FUNCTION json_object RETURNS STRING SONAME `lib_mysqludf_json.so`;
|
5、安裝gearman-mysql-udf
這個外掛是用來管理呼叫 Gearman 的分散式的佇列。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
[iyunv@redis ~]# wget -c -t 0 https://launchpad.net/gearman-my … ysql-udf-0.6.tar.gz
[iyunv@redis ~]# tar xvf gearman-mysql-udf-0.6.tar.gz
[iyunv@redis ~]# cd gearman-mysql-udf-0.6
[iyunv@redis gearman-mysql-udf-0.6]# ./configure –with-mysql=/usr/bin/mysql_config –libdir=/usr/lib64/mysql/plugin/
[iyunv@redis gearman-mysql-udf-0.6]# make
[iyunv@redis gearman-mysql-udf-0.6]# 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`);
|
6、編寫mysql觸發器(根據實際情況編寫)
1
2
3
4
5
|
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 ;
|
7、編寫gearman的worker端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
[iyunv@redis ~]# vim /www/redis_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);
}
?>
#後臺執行
[iyunv@redis www]# nohup php redis_worker.php &
|
“$redis->set($work->id, $work->name);”這條語句就是將id作KEY和name作VALUE分開儲存,需要和前面寫的php測試程式碼的存取一致。
8、更新mysql中的資料
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
mysql> set @RECV = 1;
mysql> select @RECV;
+——+
| @RECV|
+——+
| 1 |
+——+
mysql> update test set name = `ssss` where id = 1;
mysql> select @RECV;
+——+
| @RECV|
+——+
| NULL |
+——+
|
從返回值可以看到,觸發器是觸發成功的(這裡的@RECV是上面mysql TIGGER的返回值)。我們在redis中檢視資料:
1
2
3
|
[iyunv@redis redis]# ./redis-cli
127.0.0.1:6379> get 1
“sven”
|
這裡的資料居然沒有變化,這是我們就要排錯了。
1
2
3
4
5
6
7
8
9
10
11
|
[iyunv@redis ~]# vim /var/log/audit/audit.log
type=AVC msg=audit(1427807674.425:107): avc: denied { name_connect } for pid=12453 comm=”mysqld” dest=4730 scontext=unconfined_u:system_r:mysqld_t:s0 tcontext=system_u:o
bject_r:port_t:s0 tclass=tcp_socket
#看到這樣一條日誌,就知道是selinux阻止了同步
#現在將selinux的模式調成Permissive
[iyunv@redis ~]# getenforce
Enforcing
[iyunv@redis ~]# setenforce 0
[iyunv@redis ~]# getenforce
Permissive
|
設定完成以後,再次執行update,進入redis進行檢視
1
2
|
127.0.0.1:6379> get 1
“ssss”
|
重新整理一下剛剛的php介面
到這裡就基本算是大功告成了,只要application將資料寫到mysql中,mysql觸發器檢測到更新,就會通過Gearman將資料同步到redis中。然後讀取的話,就直接從redis中進行讀取。當然這只是個實驗環境,實際上還有很多細節要調整。
參考文章:http://avnpc.com/pages/mysql-replication-to-redis-by-gearman
|