Redis是一個key-value存儲系統。和Memcached相似,它支持存儲的value類型相對更多,包括string(字符串)、list(鏈表)、set(集合)和zset(有序集合)。這些數據類型都支持push/pop、add/remove及取交集並集和差集及更豐富的操做,並且這些操做都是原子性的。在此基礎上,redis支持各類不一樣方式的排序。與memcached同樣,爲了保證效率,數據都是緩存在內存中。區別的是redis會週期性的把更新的數據寫入磁盤或者把修改操做寫入追加的記錄文件,而且在此基礎上實現了master-slave(主從)同步。php
Redis 是一個高性能的key-value數據庫。 redis的出現,很大程度補償了memcached這類keyvalue存儲的不足,在部 分場合能夠對關係數據庫起到很好的補充做用。它提供了Python,Ruby,Erlang,PHP客戶端,使用很方便。html
在Redis中,並非全部的數據都一直存儲在內存中的。這是和Memcached相比一個最大的區別。Redis只會緩存全部的key的信息,若是Redis發現內存的使用量超過了某一個閥值,將觸發swap的操做,Redis根據「swappability = age*log(sizeinmemory)」計算出哪些key對應的value須要swap到磁盤。而後再將這些key對應的value持久化到磁盤中,同時在內存中清除。這種特性使得Redis能夠 保持超過其機器自己內存大小的數據。固然,機器自己的內存必需要可以保持全部的key,畢竟這些數據是不會進行swap操做的。同時因爲Redis將內存 中的數據swap到磁盤中的時候,提供服務的主線程和進行swap操做的子線程會共享這部份內存,因此若是更新須要swap的數據,Redis將阻塞這 操做,直到子線程完成swap操做後才能夠進行修改。 mysql
從Redis中讀取數據的時候,若是讀取的key對應的value不在內存中,那麼Redis就須要從swap文件中加載相應數據,而後再返回給請求方。 這裏就存在一個I/O線程池的問題。在默認的狀況下,Redis會出現阻塞,即完成全部的swap文件加載後纔會相應。這種策略在客戶端的數量較小,進行批量操做的時候比較合適。可是若是將Redis應用在一個大型的網站應用程序中,這顯然是沒法知足大併發的狀況的。因此Redis運行咱們設置I/O線程 池的大小,對須要從swap文件中加載相應數據的讀取請求進行併發操做,減小阻塞的時間。nginx
全部Redis的操做是原子的,這保證了若是兩個客戶端同時訪問的Redis服務器將得到更新後的值。Redis是一個多實用的工具,能夠在一些像緩存,消息,隊列用例中使用(Redis原生支持發佈/訂閱),在應用程序,如Web應用程序的會話,網絡頁面點擊數短時間數據等等。c++
此外對於變化頻率很是快的數據來講,若是還選擇傳統的靜態緩存方式(Memocached、File System等)展現數據,可能在緩存的存取上會有很大的開銷,並不能很好的知足須要,而Redis這樣基於內存的NoSQL數據庫,就很是適合擔任實時數據的容器。redis
在用Redis做爲Mysql的緩存時咱們能夠用gearman來進行數據的同步。sql
Gearman是一個支持分佈式的任務分發框架。設計簡潔,得到了很是普遍的支持。一個典型的Gearman應用包括如下這些部分:數據庫
Gearman Job Server:Gearman核心程序,須要編譯安裝並以守護進程形式運行在後臺json
Gearman Client:能夠理解爲任務的收件員,好比我要在後臺執行一個發送郵件的任務,能夠在程序中調用一個Gearman Client並傳入郵件的信息,而後就能夠將執行結果當即展現給用戶,而任務自己會慢慢在後臺運行。vim
Gearman Worker:任務的真正執行者,通常須要本身編寫具體邏輯並經過守護進程方式運行,Gearman Worker接收到Gearman Client傳遞的任務內容後,會按順序處理。
Gearman實現redis緩存mysql的大體流程:
首先利用mysql UDF(經過了libmysqludfjson和gearman-mysql-udf的組合實現)在mysql中的數據發生改變時觸動觸發器將數據傳入Gearman中,這時的mysql至關於Gearman的clinet。而後運行本身編寫的php程序做爲worker,將Gearman中的數據傳到Redis中去,這時的Redis至關因而Gearman的consumer。
下面redis緩存mysql的配置過程了(實驗環境rhel6.5):
Redis的安裝:
tar zxf redis-3.0.2.tar.gz yum install gcc -y cd redis-3.0.2 make make install
配置並啓動服務
cd utils/ ./install_server.sh #安裝服務啓動程序,同一臺服務器能夠有多個啓動程序 #redis的運行端口爲6379
配置Redis作mysql的緩存服務器
安裝所需的軟件包:
yum install mysql-server nginx-1.8.0-1.el6.ngx.x86_64.rpm php-5.3.3-38.el6.x86_64.rpm php-cli-5.3.3-38.el6.x86_64.rpm php-common-5.3.3-38.el6.x86_64.rpm php-devel-5.3.3-38.el6.x86_64.rpm php-fpm-5.3.3-38.el6.x86_64.rpm php-gd-5.3.3-38.el6.x86_64.rpm php-mbstring-5.3.3-38.el6.x86_64.rpm php-mysql-5.3.3-38.el6.x86_64.rpm php-pdo-5.3.3-38.el6.x86_64.rpm -y
配置php:
vim /etc/php.ini date.timezone =Asia/Shanghai vim /etc/php-fpm.d/www.conf user = nginx group = nginx unzip phpredis-master.zip cd phpredis-master phpize ./configure make make install vim /etc/php.d/redis.ini extension=redis.so /etc/init.d/php-fpm start
配置nginx:
vim /etc/nginx/conf.d/default.conf server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.php index.html index.htm; } location ~ \.php$ { root html; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /usr/share/nginx/html$fastcgi_script_name; include fastcgi_params; } /etc/init.d/nginx start
配置mysql
/etc/init.d/mysqld start mysql < test.sql #導入用於測試的數據 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'); mysql> grant all on test.* to redis@localhost identified by 'westos'; #建立用於緩存mysql數據的redis用戶 mysql -uredis -pwestos #測試這個用戶
建立測試用的php頁面:
cat /usr/share/nginx/html/index.php <?php $redis = new Redis(); $redis->connect('127.0.0.1',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('127.0.0.1','redis','westos'); 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>"; } ?>
測試結果
Redis對mysql的緩存已經完成,可是當更新了mysql,Redis不會跟着更新,就會出現mysql與redis數據不一致的狀況,接下來咱們來處理這種狀況:
配置gearman實現數據同步
安裝gearman
yum install gearmand-1.1.8-2.el6.x86_64.rpm libgearman-1.1.8-2.el6.x86_64.rpm -y /etc/init.d/gearmand start#開啓gearman它的端口是4730
安裝php的gearman模塊:
tar zxf gearman-1.1.2.tgz cd gearman-1.1.2 phpize yum install -y libgearman-devel-1.1.8-2.el6.x86_64.rpm libevent-doc-1.4.13-4.el6.noarch.rpm libevent-devel-1.4.13-4.el6.x86_64.rpm libevent-headers-1.4.13-4.el6.noarch.rpm #解決依賴性 ./configure make make install vim /etc/php.d/gearman.ini extension=gearman.so /etc/init.d/php-fpm reload
安裝lib_mysqludef_json將mysql數據轉換爲Redis能用的JSON格式
yum install mysql-devel -y unzip lib_mysqludf_json-master.zip cd lib_mysqludf_json-master gcc $(mysql_config --cflags) -shared -fPIC -o lib_mysqludf_json.so lib_mysqludf_json.c
將lib_mysqludf_json.so模塊拷貝到mysql的插件目錄
cp lib_mysqludf_json.so /usr/lib64/mysql/plugin/
註冊UDF函數(只用於mysql5.7之前的版本,5.7之後mysql自帶JSON格式)
mysql> CREATE FUNCTION json_object RETURNS STRING SONAME 'lib_mysqludf_json.so'; mysql> select * from mysql.func;#查看函數
安裝 gearman-mysql-udf來調用Gearman的分佈式隊列
tar zxf gearman-mysql-udf-0.6.tar.gz cd gearman-mysql-udf-0.6 yum install gcc-c++ -y ./configure --libdir=/usr/lib64/mysql/plugin/ make make install
註冊UDF函數:
mysql> 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> select * from mysql.func;#查看函數
mysql> SELECT gman_servers_set('127.0.0.1:4730');#指定gearman的服務信息,若是是遠程就填寫遠程IP
編寫mysql觸發器:
mysql <test.sql cat test.sql use test; 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 ;
最後配置gearman的worker端並使它一直運行在後端
cat /usr/local/bin/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); } ?> nohup php worker.php &
測試:更新mysql數據庫能夠看到redisye跟着更新了
mysql> use test mysql> update test set name='hello' where id=1; [root@server1 bin]# redis-cli 127.0.0.1:6379> get 1 "hello"
刷新頁面數據也跟着更新了