標籤:set enable java upd zip release png war mirrors
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,工作量更小。
1、安裝依賴
yum install -y boost-devel gperf libevent-devel libuuid-devel
yum install mysql-devel -y
2、下載gearman
wget https://launchpad.net/gearmand/1.2/1.1.12/+download/gearmand-1.1.12.tar.gz
3、編譯安裝,指定mysqlclient的連結路徑
tar -zxvf gearmand-1.1.12.tar.gz
cd gearmand-1.1.12
./configure
make && make install
4、啟動gearmand服務端 (啟動之時,在/var/log/下建立gearmand.log記錄檔。-l 指定記錄檔 -d後台運行 -L 0.0.0.0 綁定到IPV4
gearmand -L 0.0.0.0 -l /var/log/gearmand.log -d
5、查看是否啟動成功
ps -ef | grep gearman
6、查看是否安裝成功,查看gearman版本資訊
gearmand -V
7、MySQL UDF + Trigger同步資料到Gearman (https://github.com/mysqludf)
安裝lib_mysqludf_json(lib_mysqludf_json可以把MySQL表的資料以json資料格式輸出)
wget https://github.com/mysqludf/lib_mysqludf_json/archive/master.zip
unzip master.zip
cd lib_mysqludf_json-master/
rm -rf lib_mysqludf_json.so
8、編譯 mysql_config 這是mysql的設定檔,可以 find /usr -name mysql_config 搜尋下在什麼位置
gcc $(/usr/local/mysql/bin/mysql_config --cflags) -shared -fPIC -o lib_mysqludf_json.so lib_mysqludf_json.c
9、拷貝lib_mysqludf_json.so到MySQL的plugin目錄
(可以登陸MySQL,輸入命令"show variables like ‘%plugin%‘"查看plugin位置)
cp lib_mysqludf_json.so /usr/local/mysql/lib/plugin/
示範lib_mysqludf_json功能
登入mysql
mysql -uroot -h127.0.0.1 -p
註冊UDF函數
CREATE FUNCTION json_object RETURNS STRING SONAME "lib_mysqludf_json.so";
CREATE FUNCTION json_array RETURNS STRING SONAME "lib_mysqludf_json.so";
CREATE FUNCTION json_members RETURNS STRING SONAME "lib_mysqludf_json.so";
CREATE FUNCTION json_values RETURNS STRING SONAME "lib_mysqludf_json.so";
//json_array|json_members|json_values函數註冊方式與json_object一樣.
select json_object(id,file_save_type,base_dir) as sys_file_save_config from sys_file_save_config;
ERROR 1123 (HY000): Can‘t initialize function ‘json_object‘; Invalid json member name - name cannot be empty
以上錯誤這樣解決,給每個成員名稱使用別名即可:
select json_object(id as id ,file_save_type as fileSaveType,app_id as appID) as sys_file_save_config from sys_file_save_config;
10、安裝gearman-mysql-udf (https://launchpad.net/gearman-mysql-udf)
wget https://launchpad.net/gearman-mysql-udf/trunk/0.6/+download/gearman-mysql-udf-0.6.tar.gz
tar zxvf gearman-mysql-udf-0.6.tar.gz
cd gearman-mysql-udf-0.6
11、安裝libgearman-devel
yum install libgearman-devel -y
如果沒有yum源,添加epel.repo yum源
[epel]
name=Extra Packages for Enterprise Linux 6 - $basearch
#baseurl=http://download.fedoraproject.org/pub/epel/6/$basearch
mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-6&arch=$basearch
failovermethod=priority
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6
[epel-debuginfo]
name=Extra Packages for Enterprise Linux 6 - $basearch - Debug
#baseurl=http://download.fedoraproject.org/pub/epel/6/$basearch/debug
mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-debug-6&arch=$basearch
failovermethod=priority
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6
gpgcheck=1
[epel-source]
name=Extra Packages for Enterprise Linux 6 - $basearch - Source
#baseurl=http://download.fedoraproject.org/pub/epel/6/SRPMS
mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-source-6&arch=$basearch
failovermethod=priority
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6
gpgcheck=1
12、編譯安裝
(可以登陸MySQL,輸入命令"show variables like ‘%plugin%‘"查看plugin位置, mysql_config的設定檔,以及外掛程式庫所在路徑,編譯之後會在此路徑產生.so檔案)
./configure --with-mysql=/usr/local/mysql/bin/mysql_config --libdir=/usr/local/mysql/lib/plugin/
make && make install
示範gearman-mysql-udf功能
mysql -uroot -p
CREATE FUNCTION gman_do_background RETURNS STRING SONAME "libgearman_mysql_udf.so";
CREATE FUNCTION gman_servers_set RETURNS STRING SONAME "libgearman_mysql_udf.so";
CREATE FUNCTION gman_do RETURNS STRING SONAME "libgearman_mysql_udf.so";
CREATE FUNCTION gman_do_high RETURNS STRING SONAME "libgearman_mysql_udf.so";
CREATE FUNCTION gman_do_low RETURNS STRING SONAME "libgearman_mysql_udf.so";
CREATE FUNCTION gman_do_high_background RETURNS STRING SONAME "libgearman_mysql_udf.so";
CREATE FUNCTION gman_do_low_background RETURNS STRING SONAME "libgearman_mysql_udf.so";
CREATE FUNCTION gman_sum RETURNS STRING SONAME "libgearman_mysql_udf.so";
//函數gman_do|gman_do_high|gman_do_low|gman_do_high_background|gman_do_low_background|gman_sum註冊方式類似,請參考gearman-mysql-udf-0.6/README
//指定gearman job server地址
SELECT gman_servers_set(‘127.0.0.1:4730‘);
如果出現異常資訊:
ERROR 1126 (HY000): Can‘t open shared library ‘libgearman_mysql_udf.so‘ (errno: 11 libgearman.so.8: cannot open shared object file: No such file or directory)
表示系統找不到 libgearman.so 檔案,一般so都在/usr/local/lib目錄下,修改設定檔/etc/ld.so.conf,將/usr/local/lib目錄加入進去即可:
$ cat /etc/ld.so.conf
include ld.so.conf.d/*.conf
/usr/local/lib
$ /sbin/ldconfig -v | grep gearman*
13、MySQL Trigger調用Gearman UDF實現同步
建立觸發器
DELIMITER $$
CREATE TRIGGER test_data_to_redis AFTER UPDATE ON test FOR EACH ROW BEGIN
[email protected]=gman_do_background(‘syncToRedis‘, json_object(NEW.id AS `id`, NEW.phone AS`phone`));
END$$;
DELIMITER $$
CREATE TRIGGER test_data_to_redis2 AFTER INSERT ON test
FOR EACH ROW BEGIN
SET @ret=gman_do_background(‘syncToRedis2‘, json_object(NEW.id AS `id`, NEW.phone AS`phone`));
END$$
DELIMITER ;
DELIMITER $$
CREATE TRIGGER test_data_to_redis3 BEFORE DELETE ON test
FOR EACH ROW BEGIN
SET @ret=gman_do_background(‘syncToRedis3‘, json_object(OLD.id AS `id`, OLD.phone AS`phone`));
END$$
DELIMITER ;
說明以及問題:此類採用了gearman官網的java-gearman-service(地址:https://launchpad.net/gearman-java),目前release版本是0.6.6。java-gearman-servic.jar包中,即包括gearman server,還包括client和work用戶端API。
問題:config類為spring注入的設定檔類,在worker.addFunction中,如果通過config類的屬性,並且屬性是從設定檔來的就會有問題。不知道為啥,寫死就是OK的。此類串連遠端gearman job server。
jar包需要添加到本地jar倉庫:
mvn install:install-file -Dfile=C:\software\java-gearman-service-0.6.6.jar -DgroupId=org.gearman.jgs -DartifactId=java-gearman-service -Dversion=0.6.6 -Dpackaging=jar
import java.util.concurrent.TimeUnit;
import org.gearman.Gearman;
import org.gearman.GearmanFunction;
import org.gearman.GearmanFunctionCallback;
import org.gearman.GearmanServer;
import org.gearman.GearmanWorker;
/**
* *ECHO_HOST = "192.168.125.131"為安裝了Gearman並開啟geramand服務的主機地址
*int ECHO_PORT = 4730預設連接埠為4730
*
* @author Administrator
*
*/
public class EchoWorker implements GearmanFunction {
// function name
public static final String ECHO_FUNCTION_NAME = "syncToRedis";
// job server地址
public static final String ECHO_HOST = "192.168.1.245";
// job server監聽的連接埠
public static final int ECHO_PORT = 4730;
public static void main(String[] args) {
// 建立一個Gearman執行個體
Gearman gearman = Gearman.createGearman();
/*
* 建立一個jobserver
*
* Parameter 1: job server的IP地址 Parameter 2: job server監聽的連接埠
*
* job server收到client的job,並將其分發給註冊worker
*
*/
GearmanServer server = gearman.createGearmanServer(EchoWorker.ECHO_HOST, EchoWorker.ECHO_PORT);
// 建立一個Gearman的worker
GearmanWorker worker = gearman.createGearmanWorker(); // 正題來了,建立work節點。
worker.setReconnectPeriod(2, TimeUnit.SECONDS); // 設定逾時重連時間
worker.setMaximumConcurrency(5); // 最大並發數
// 告訴工人如何執行工作(主要實現了GearmanFunction介面)
worker.addFunction(EchoWorker.ECHO_FUNCTION_NAME, new EchoWorker());
// worker串連伺服器
worker.addServer(server);
}
@Override
public byte[] work(String function, byte[] data, GearmanFunctionCallback callback) throws Exception {
// work方法實現了GearmanFunction介面中的work方法,本執行個體中進行了字串的反寫
if (data != null) {
String str = new String(data);
System.out.println(str);
StringBuffer sb = new StringBuffer(str);
return sb.reverse().toString().getBytes();
} else {
return "未接收到data".getBytes();
}
}
}
import org.gearman.Gearman;
import org.gearman.GearmanClient;
import org.gearman.GearmanJobEvent;
import org.gearman.GearmanJobReturn;
import org.gearman.GearmanServer;
public class EchoClient {
public static void main(String... args) throws InterruptedException {
//建立一個Gearman執行個體
Gearman gearman = Gearman.createGearman();
//建立一個Gearman client
GearmanClient client = gearman.createGearmanClient();
/*
* 建立一個jobserver
*
* Parameter 1: job server的IP地址
* Parameter 2: job server監聽的連接埠
*
*job server收到client的job,並將其分發給註冊worker
*
*/
GearmanServer server = gearman.createGearmanServer(
EchoWorker.ECHO_HOST, EchoWorker.ECHO_PORT);
// 告訴用戶端,提交工作時它可以串連到該伺服器
client.addServer(server);
/*
* 向job server提交工作
*
* Parameter 1: gearman function名字
* Parameter 2: 傳送給job server和worker的資料
*
* GearmanJobReturn返回job發熱結果
*/
GearmanJobReturn jobReturn = client.submitJob(
EchoWorker.ECHO_FUNCTION_NAME, ("Hello World!").getBytes());
//遍曆作業事件,直到我們打到最後檔案
while (!jobReturn.isEOF()) {
//下一個作業事件
GearmanJobEvent event = jobReturn.poll();
switch (event.getEventType()) {
case GEARMAN_JOB_SUCCESS: //job執行成功
System.out.println(new String(event.getData()));
break;
case GEARMAN_SUBMIT_FAIL: //job提交失敗
case GEARMAN_JOB_FAIL: //job執行失敗
System.err.println(event.getEventType() + ": "
+ new String(event.getData()));
default:
}
}
//關閉
gearman.shutdown();
}
}
http://gearman.org/download/
php方案:https://www.tuicool.com/articles/B7Jjaa
更多好文章分享
JAVA通過Gearman實現MySQL到Redis的資料同步(非同步複製)