標籤:
一、redis簡介
Redis是一個key-value儲存系統。和Memcached類似,為了保證效率,資料都是緩衝在記憶體中。區別的是redis會周期性的把更新的資料寫入磁碟或者把修改操作寫入追加的記錄檔案,並且在此基礎上實現了master-slave(主從)同步。在部分場合可以對關聯式資料庫起到很好的補充作用。它提供了Java,C/C++(hiredis),C#,PHP,JavaScript,Perl,Object-C,Python,Ruby等用戶端,使用很方便。
二、架構圖
<ignore_js_op>
大致結構就是讀寫分離,將mysql中的資料通過觸發器同步到redis中
三、安裝LNMP環境(這裡為了省事,就是用yum來安裝)
1、修改yum源
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[[email protected] ~]# 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 |
[[email protected] ~]# 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 |
[[email protected] ~]# 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 |
[[email protected] ~]# sed -i ‘s/apache/nginx/g‘ /etc/php-fpm.d/www.conf [[email protected] ~]# /etc/init.d/php-fpm start 正在啟動 php-fpm: [確定]
[[email protected] ~]# /etc/init.d/mysqld start 正在啟動 mysqld: [確定]
[[email protected] ~]# mkdir /{logs,www} [[email protected] ~]# chown -R nginx:nginx /{logs,www} [[email protected] ~]# /etc/init.d/nginx start 正在啟動 nginx: [確定]
[[email protected] www]# service iptables stop iptables: Flushing firewall rules: [ OK ] iptables: Setting chains to policy ACCEPT: filter [ OK ] iptables: Unloading modules: [ OK ]
[[email protected] 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 |
[[email protected] ~]# mysql mysql> grant all privileges on *.* to [email protected] identified by ‘123456‘;
mysql> flush privileges;
|
6、測試
1 2 3 4 |
[[email protected] ~]# vim /www/index.php <?php phpinfo(); ?>
|
然後訪問頁面看到php的相關資訊,基礎環境就算搭建完成了。
四、安裝redis
1、安裝redis
1 2 3 4 5 6 7 8 9 10 11 12 |
[[email protected] ~]# wget -c -t 0 http://download.redis.io/releases/redis-2.8.19.tar.gz
[[email protected] ~]# mkdir /usr/local/redis
[[email protected] ~]# tar xvf redis-2.8.19.tar.gz #安裝很簡單、直接make就可以了 [[email protected] ~]# cd redis-2.8.19 [[email protected] redis-2.8.19]# make
#編譯完成後,將src中的可執行檔拷貝到剛剛建立的目錄中 [[email protected] src]# cp redis-benchmark redis-check-aof redis-check-dump redis-cli redis-sentinel redis-server /usr/local/redis/ [[email protected] 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 |
[[email protected] ~]# wget -c -t 0 https://github.com/owlient/phpredis/archive/master.zip [[email protected] ~]# unzip master.zip
[[email protected] ~]# cd phpredis-master/ [[email protected] phpredis-master]# phpize [[email protected] phpredis-master]# ./configure --with-php-config=/usr/bin/php-config [[email protected] phpredis-master]# make && make install
#修改php的設定檔,如果沒有“extension=redis.so”,就加上這一行 [[email protected] ~]# vim /etc/php.ini extension=redis.so
[[email protected] ~]# /etc/init.d/php-fpm restart 停止 php-fpm: [確定] 正在啟動 php-fpm: [確定]
|
3、是否安裝成功
還是訪問phpinfo的那個介面
<ignore_js_op>
看到這個就是安裝完成了。
五、讀寫分離
這裡只是簡單的做了一下讀,沒有寫操作的相關代碼,過一會測試,直接到資料庫裡執行update來類比寫操作。
1、在mysql中插入一些測試資料
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
[[email protected] ~]# 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時
<ignore_js_op>
再次訪問,此時redis中已有相關資料
<ignore_js_op>
到這裡,我們已經實現了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 |
[[email protected] ~]# yum -y install gearmand libgearman-devel [[email protected] ~]# /etc/init.d/gearmand start 正在啟動 gearmand: [確定] [[email protected] ~]# /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 |
[[email protected] ~]# wget -c -t 0 https://pecl.php.net/get/gearman-1.1.1.tgz
[[email protected] ~]# tar xvf gearman-1.1.1.tgz [[email protected] ~]# cd gearman-1.1.1 [[email protected] gearman-1.1.1]# phpize [[email protected] gearman-1.1.1]# ./configure --with-php-config=/usr/bin/php-config [[email protected] gearman-1.1.1]# make [[email protected] gearman-1.1.1]# make install
#如果php的設定檔中沒有extension = gearman.so,就加上此行 [[email protected] ~]# vim /etc/php.ini extension = gearman.so [[email protected] ~]# /etc/init.d/php-fpm restart 停止 php-fpm: [確定] 正在啟動 php-fpm: [確定]
|
<ignore_js_op>
這樣就是安裝成功了
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 |
[[email protected] ~]# wget -c -t 0 https://github.com/mysqludf/lib_mysqludf_json/archive/master.zip [[email protected] ~]# unzip master.zip [[email protected] ~]# cd lib_mysqludf_json-master/ [[email protected] 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的標頭檔在哪裡 [[email protected] lib_mysqludf_json-master]# yum -y install mysql-devel [[email protected] 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 | +---------------+-------------------------+
#將模組拷貝到外掛程式目錄下 [[email protected] 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 |
[[email protected] ~]# wget -c -t 0 https://launchpad.net/gearman-my ... ysql-udf-0.6.tar.gz [[email protected] ~]# tar xvf gearman-mysql-udf-0.6.tar.gz
[[email protected] ~]# cd gearman-mysql-udf-0.6 [[email protected] gearman-mysql-udf-0.6]# ./configure --with-mysql=/usr/bin/mysql_config --libdir=/usr/lib64/mysql/plugin/ [[email protected] gearman-mysql-udf-0.6]# make [[email protected] 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 |
[[email protected] ~]# 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); } ?>
#後台運行 [[email protected] 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 |
[[email protected] redis]# ./redis-cli 127.0.0.1:6379> get 1 "sven"
|
這裡的資料居然沒有變化,這是我們就要排錯了。
1 2 3 4 5 6 7 8 9 10 11 |
[[email protected] ~]# 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 [[email protected] ~]# getenforce Enforcing [[email protected] ~]# setenforce 0 [[email protected] ~]# getenforce Permissive
|
設定完成以後,再次執行update,進入redis進行查看
1 2 |
127.0.0.1:6379> get 1 "ssss"
|
重新整理一下剛剛的php介面
<ignore_js_op>
到這裡就基本算是大功告成了,只要application將資料寫到mysql中,mysql觸發器檢測到更新,就會通過Gearman將資料同步到redis中。然後讀取的話,就直接從redis中進行讀取。當然這隻是個實驗環境,實際上還有很多細節要調整。
redis作為mysql的快取服務器(讀寫分離,通過mysql觸發器實現資料同步)