通過Gearman實現MySQL到Redis的資料同步
對於變化頻率非常快的資料來說,如果還選擇傳統的靜態緩衝方式(Memocached、File System等)展示資料,可能在緩衝的存取上會有很大的開銷,並不能很好的滿足需要,而Redis這樣基於記憶體的NoSQL資料庫,就非常適合擔任即時資料的容器。
但是往往我們又有資料可靠性的需求,採用MySQL作為資料存放區,不會因為記憶體問題而引起資料丟失,同時也可以利用關聯式資料庫的特性實現很多功能。
所以就會很自然的想到是否可以採用MySQL作為資料存放區引擎,Redis則作為Cache。而這種需求目前還沒有看到有特別成熟的解決方案或工具,因此本文將嘗試採用Gearman+PHP+MySQL UDF的組合非同步實現MySQL到Redis的資料複製。
MySQL到Redis資料複製方案
無論MySQL還是Redis,自身都帶有資料同步的機制,像比較常用的MySQL的Master/Slave模式,就是由Slave端分析Master的binlog來實現的,這樣的資料複製其實還是一個非同步過程,只不過當伺服器都在同一內網時,非同步延遲幾乎可以忽略。
那麼理論上我們也可以用同樣方式,分析MySQL的binlog檔案並將資料插入Redis。但是這需要對binlog檔案以及MySQL有非常深入的理解,同時由於binlog存在Statement/Row/Mixedlevel多種形式,分析binlog實現同步的工作量是非常大的。
因此這裡選擇了一種開發成本更加低廉的方式,借用已經比較成熟的MySQL UDF,將MySQL資料首先放入Gearman中,然後通過一個自己編寫的PHP Gearman Worker,將資料同步到Redis。比分析binlog的方式增加了不少流程,但是實現成本更低,更容易操作。
Gearman的安裝與使用
Gearman是一個支援分布式的任務分發架構。設計簡潔,獲得了非常廣泛的支援。一個典型的Gearman應用程式套件括以下這些部分:
- Gearman Job Server:Gearman核心程式,需要編譯安裝並以守護進程形式運行在後台
- Gearman Client:可以理解為任務的收件員,比如我要在後台執行一個發送郵件的任務,可以在程式中調用一個Gearman Client並傳入郵件的資訊,然後就可以將執行結果立即展示給使用者,而任務本身會慢慢在後台運行。
- Gearman Worker:任務的真正執行者,一般需要自己編寫具體邏輯並通過守護進程方式運行,Gearman Worker接收到Gearman Client傳遞的任務內容後,會按順序處理。
以前曾經介紹過類似的背景工作處理項目Resque。兩者的設計其實非常接近,簡單可以類比為:
- Gearman Job Server:對應Resque的Redis部分
- Gearman Client:對應Resque的Queue操作
- Gearman Worker:對應Resque的Worker和Job
這裡之所以選擇Gearman而不是Resque是因為Gearman提供了比較好用的MySQL UDF,工作量更小。
安裝Gearman及PHP Gearman擴充
以下均以Ubuntu12.04為例。
apt-get install gearman gearman-server libgearman-dev
檢查Gearman的健全狀態:
/etc/init.d/gearman-job-server status* gearmand is running
說明Gearman已經安裝成功。
PHP的Gearman擴充可以通過pecl直接安裝
pecl install gearmanecho "extension=gearman.so">/etc/php5/conf.d/gearman.iniservice php5-fpm restart
但是實測發現ubuntu預設安裝的gearman版本過低,直接運行pecl install gearman會報錯
configure: error: libgearman version 1.1.0or later required
因此Gearman + PHP擴充建議通過編譯方式安裝,這裡為了簡單說明,選擇安裝舊版本擴充:
pecl install gearman-1.0.3
Gearman + PHP執行個體
為了更容易理解後文Gearman的運行流程,這裡不妨從一個最簡單的Gearman執行個體來說明,比如我們要進行一個檔案處理的操作,首先編寫一個Gearman Client並命名為client.php:
<?php$client =newGearmanClient();$client->addServer();$client->doBackground('writeLog','Log content');echo '檔案已經在後台操作';
運行這個檔案,相當於類比使用者請求一個Web頁面後,將處理結束的資訊返回使用者:
php client.php
查看一下Gearman的狀況:
(echo status ; sleep 0.1)| netcat127.0.0.14730
可以看到輸出為
writeLog 100.
說明我們已經在Gearman中建立了一個名為writeLog的任務,並且有1個任務在隊列等待中。
而上面的4列分別代表當前的Gearman的運行狀態:
- 任務名稱
- 在等待隊列中的任務
- 正在啟動並執行任務
- 正在啟動並執行Worker進程
可以使用watch進行即時監控:
watch -n 1"(echo status; sleep 0.1) | nc 127.0.0.1 4730"
然後我們需要編寫一個Gearman Worker命名為worker.php:
<?php$worker =newGearmanWorker();$worker->addServer();$worker->addFunction('writeLog','writeLog');while($worker->work());function writeLog($job){ $log = $job->workload();file_put_contents(__DIR__ .'/gearman.log', $log ."\n", FILE_APPEND | LOCK_EX);}
Worker使用一個while死迴圈實現守護進程,運行
php worker.php
可以看到Gearman狀態變為:
writeLog 001
同時查看同目錄下gearman.log,內容應為從Client傳入的值Log content
。
通過MySQL UDF + Trigger同步資料到Gearman
MySQL要實現與外部程式互連的最好方式還是通過MySQL UDF(MySQL user defined functions)來實現。為了讓MySQL能將資料傳入Gearman,這裡使用了lib_mysqludf_json和gearman-mysql-udf的組合。
安裝lib_mysqludf_json
使用lib_mysqludf_json的原因是因為Gearman只接受字串作為入口參數,可以通過lib_mysqludf_json將MySQL中的資料編碼為JSON字串
apt-get install libmysqlclient-devwget https://github.com/mysqludf/lib_mysqludf_json/archive/master.zipunzip master.zipcd lib_mysqludf_json-master/rm lib_mysqludf_json.sogcc $(mysql_config --cflags)-shared -fPIC -o lib_mysqludf_json.so lib_mysqludf_json.c
可以看到重新編譯產生了 lib_mysqludf_json.so 檔案,此時需要查看MySQL的外掛程式安裝路徑:
mysql -u root -pPASSWORD --execute="show variables like '%plugin%';"+---------------+------------------------+|Variable_name|Value|+---------------+------------------------+| plugin_dir |/usr/lib/mysql/plugin/|+---------------+------------------------+
然後將 lib_mysqludf_json.so 檔案複製到對應位置:
cp lib_mysqludf_json.so /usr/lib/mysql/plugin/
最後登入MySQL運行語句註冊UDF函數:
CREATE FUNCTION json_object RETURNS STRING SONAME 'lib_mysqludf_json.so';
安裝gearman-mysql-udf
方法幾乎一樣:
apt-get install libgearman-devwget https://launchpad.net/gearman-mysql-udf/trunk/0.6/+download/gearman-mysql-udf-0.6.tar.gztar -xzf gearman-mysql-udf-0.6.tar.gzcd gearman-mysql-udf-0.6./configure --with-mysql=/usr/bin/mysql_config --libdir=/usr/lib/mysql/plugin/make && make install
登入MySQL運行語句註冊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';
最後指定Gearman伺服器的資訊:
SELECT gman_servers_set('127.0.0.1:4730');
通過MySQL觸發器實現資料同步
最終同步哪些資料,同步的條件,還是需要根據實際情況決定,比如我希望將資料表data的資料在每次更新時同步,那麼編寫Trigger如下:
DELIMITER $$CREATE TRIGGER datatoredis AFTER UPDATE ON data FOR EACH ROW BEGIN SET @ret=gman_do_background('syncToRedis', json_object(NEW.id as`id`, NEW.volume as`volume`));END$$DELIMITER ;
嘗試在資料庫中更新一條資料查看Gearman是否生效。
Gearman PHP Worker將MySQL資料非同步複製到Redis
Redis作為時下當熱的NoSQL緩衝解決方案無需過多介紹,其安裝及使用也非常簡單:
apt-get install redis-server pecl install redisecho "extension=redis.so">/etc/php5/conf.d/redis.ini
然後編寫一個Gearman Worker:redis_worker.php
#!/usr/bin/env php<?$worker =newGearmanWorker();$worker->addServer();$worker->addFunction('syncToRedis','syncToRedis');$redis =newRedis();$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)){returnfalse;} $redis->set($work->id, $workString);}
最後需要將Worker在後台運行:
nohup php redis_worker.php &
通過這種方式將MySQL資料複製到Redis,經測試單Worker基本可以瞬時完成。
Ubuntu 14.04下Redis安裝及簡單測試
Redis叢集明細文檔
Ubuntu 12.10下安裝Redis(圖文詳解)+ Jedis串連Redis
Redis系列-安裝部署維護篇
CentOS 6.3安裝Redis
Redis安裝部署學習筆記
Redis設定檔redis.conf 詳解
Redis 的詳細介紹:請點這裡
Redis 的:請點這裡
from:http://avnpc.com/pages/mysql-replication-to-redis-by-gearman%20
本文永久更新連結地址: