經過Gearman實現MySQL到Redis的數據複製

對於變化頻率很是快的數據來講,若是還選擇傳統的靜態緩存方式(File System等)展現數據,可能在緩存的存取上會有很大的開銷,並不能很好的知足須要,而Redis這樣基於內存的NoSQL數據庫,就很是適合擔任實時數據的容器。php

可是每每咱們又有數據可靠性的需求,採用MySQL做爲數據存儲,不會由於內存問題而引發數據丟失,同時也能夠利用關係數據庫的特性實現不少功能。mysql

因此就會很天然的想到是否能夠採用MySQL做爲數據存儲引擎,Redis則做爲Cache。而這種需求目前尚未看到有特別成熟的解決方案或工具,所以本文將嘗試採用Gearman+PHP+MySQL UDF的組合異步實現MySQL到Redis的數據複製。git

###MySQL到Redis數據複製方案 不管MySQL仍是Redis,自身都帶有數據同步的機制,像比較經常使用的MySQL的Master/Slave模式,就是由Slave端分析Master的binlog來實現的,這樣的數據複製其實仍是一個異步過程,只不過當服務器都在同一內網時,異步的延遲幾乎能夠忽略。github

那麼理論上咱們也能夠用一樣方式,分析MySQL的binlog文件並將數據插入Redis。可是這須要對binlog文件以及MySQL有很是深刻的理解,同時因爲binlog存在Statement/Row/Mixedlevel多種形式,分析binlog實現同步的工做量是很是大的。redis

所以這裏選擇了一種開發成本更加低廉的方式,借用已經比較成熟的MySQL UDF,將MySQL數據首先放入Gearman中,而後經過一個本身編寫的PHP Gearman Worker,將數據同步到Redis。比分析binlog的方式增長了很多流程,可是實現成本更低,更容易操做。sql

###Gearman的安裝與使用 Gearman是一個支持分佈式的任務分發框架。設計簡潔,得到了很是普遍的支持。一個典型的Gearman應用包括如下這些部分:數據庫

###Gearman構架json

Gearman Job Server:Gearman核心程序,須要編譯安裝並以守護進程形式運行在後臺 Gearman Client:能夠理解爲任務的收件員,好比我要在後臺執行一個發送郵件的任務,能夠在程序中調用一個Gearman Client並傳入郵件的信息,而後就能夠將執行結果當即展現給用戶,而任務自己會慢慢在後臺運行。 Gearman Worker:任務的真正執行者,通常須要本身編寫具體邏輯並經過守護進程方式運行,Gearman Worker接收到Gearman Client傳遞的任務內容後,會按順序處理。 之前曾經介紹過相似的後臺任務處理項目Resque。二者的設計其實很是接近,簡單能夠類比爲:ubuntu

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 gearman echo "extension=gearman.so" > /etc/php5/conf.d/gearman.ini service php5-fpm restart 可是實測發現ubuntu默認安裝的gearman版本太低,直接運行pecl install gearman會報錯

configure: error: libgearman version 1.1.0 or later required 所以Gearman + PHP擴展建議經過編譯方式安裝,這裏爲了簡單說明,選擇安裝舊版本擴展:

pecl install gearman-1.0.3 ###Gearman + PHP實例 爲了更容易理解後文Gearman的運行流程,這裏不妨從一個最簡單的Gearman實例來講明,好比咱們要進行一個文件處理的操做,首先編寫一個Gearman Client並命名爲client.php:

<?php
$client = new GearmanClient();
$client->addServer();
$client->doBackground('writeLog', 'Log content');
echo '文件已經在後臺操做';

運行這個文件,至關於模擬用戶請求一個Web頁面後,將處理結束的信息返回用戶:

php client.php 查看一下Gearman的情況:

(echo status ; sleep 0.1) | netcat 127.0.0.1 4730

能夠看到輸出爲

writeLog        1       0       0

說明咱們已經在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 = new GearmanWorker();
$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        0       0       1

同時查看同目錄下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-dev
wget https://github.com/mysqludf/lib_mysqludf_json/archive/master.zip
unzip master.zip
cd lib_mysqludf_json-master/
rm lib_mysqludf_json.so
gcc $(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-dev
wget https://launchpad.net/gearman-mysql-udf/trunk/0.6/+download/gearman-mysql-udf-0.6.tar.gz
tar -xzf gearman-mysql-udf-0.6.tar.gz
cd 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 redis
echo "extension=redis.so" > /etc/php5/conf.d/redis.ini

而後編寫一個Gearman Worker:redis_worker.php

#!/usr/bin/env 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, $workString);
}

最後須要將Worker在後臺運行:

nohup php redis_worker.php &

經過這種方式將MySQL數據複製到Redis,經測試單Worker基本能夠瞬時完成。

注意點 在實際操做中發現,Gearman UDF在每次MySQL服務重啓後會丟失已經設置的服務器信息。由於時間有限沒有深刻的調查緣由,而用了曲線救國的解決方法,讓MySQL在每次服務啓動時自動運行一次設置語句:

vi /var/lib/mysql/init_file.sql

加入

SELECT gman_servers_set('127.0.0.1:4730');

而後在/etc/mysql/my.cnf的[mysqld]小節下加入

init-file=/var/lib/mysql/init_file.sql

而後重啓服務。

相關文章
相關標籤/搜索