《阿里巴巴的增量訂閱&消費組件》 https://github.com/alibaba/canal java
早期,阿里巴巴B2B公司由於存在杭州和美國雙機房部署,存在跨機房同步的業務需求。不過早期的數據庫同步業務,主要是基於trigger的方式獲取增量變動,不過從2010年開始,阿里系公司開始逐步的嘗試基於數據庫的日誌解析,獲取增量變動進行同步,由此衍生出了增量訂閱&消費的業務,今後開啓了一段新紀元。mysql
名稱:運河[kə'næl]git
譯意:水道/管道/溝渠github
語言:純java開發redis
定位:基於數據庫增量日誌解析,提供增量數據訂閱&消費,目前主要支持了mysqlsql
關鍵詞:mysql binlog解析器/實時/隊列和主題數據庫
基於日誌增量訂閱&消費支持的業務:apache
1.數據庫鏡像
2.數據庫實時備份
3.多級索引 (賣家和買家各自分庫索引)
4.search build
5.業務cache刷新
6.價格變化等重要業務消息segmentfault
從上層來看,複製分紅三步:less
master將改變記錄到二進制日誌(binary log)中(這些記錄叫作二進制日誌事件,binary log events,能夠經過show binlog events進行查看);
slave將master的binary log events拷貝到它的中繼日誌(relay log);
slave重作中繼日誌中的事件,將改變反映它本身的數據。
CentOs7.3 搭建 MySQL 5.7.19 主從複製,以及複製實現細節分析
原理相對比較簡單:
1.運河模擬mysql slave的交互協議,假裝本身爲mysql slave,向mysql master發送dump協議
2.mysql master收到dump請求,開始推送二進制日誌給slave(也就是運河)
3.運河解析二進制對象(原始爲字節流)
canal的原理是基於mysql binlog技術,因此這裏必定須要開啓mysql的binlog寫入功能,建議配置binlog模式爲row.
針對阿里雲RDS帳號默認已經有binlog dump權限,不須要任何權限或者binlog設置,能夠直接跳過這一步
修改 etc/my.cnf
$ cat /etc/my.cnf [mysqld] log-bin=mysql-bin #添加這一行就ok binlog-format=ROW #選擇row模式 server_id=1 #配置mysql replaction須要定義,不能和canal的slaveId重複
MySQL 安裝
《CentOs7.3 安裝 MySQL 5.7.19 二進制版本》
直接下載 訪問:https://github.com/alibaba/canal/releases,會列出全部歷史的發佈版本包 下載方式,好比以1.0.24版本爲例子:
$ ca /opt $ wget https://github.com/alibaba/canal/releases/download/canal-1.0.24/canal.deployer-1.0.24.tar.gz
or 本身編譯
$ git clone git@github.com:alibaba/canal.git $ cd canal; $ mvn clean install -Dmaven.test.skip -Denv=release
編譯完成後,會在根目錄下產生target/canal.deployer-$version.tar.gz
$ mkdir /opt/canal $ tar zxvf canal.deployer-$version.tar.gz -C /opt/canal
應用參數:
$ vi conf/example/instance.properties
################################################# ## mysql serverId canal.instance.mysql.slaveId = 1234 canal.instance.master.address 須要改爲本身的數據庫信息 canal.instance.master.address = 127.0.0.1:3306 canal.instance.master.journal.name = canal.instance.master.position = canal.instance.master.timestamp = #canal.instance.standby.address = #canal.instance.standby.journal.name = #canal.instance.standby.position = #canal.instance.standby.timestamp = username/password,須要改爲本身的數據庫信息 canal.instance.dbUsername = canal canal.instance.dbPassword = canal canal.instance.defaultDatabaseName = canal.instance.connectionCharset = UTF-8
$ sh bin/startup.sh
$ less logs/canal/canal.log
$ less logs/example/example.log
$ sh bin/stop.sh
本測試項目,選擇的是Redis 單機服務。集羣也支持
CentOs7.3 搭建 Redis-4.0.1 Cluster 集羣服務
CREATE DATABASE `test`; use `test`; DROP TABLE IF EXISTS `test`; CREATE TABLE `test` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(1000) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of test -- ---------------------------- INSERT INTO `test` VALUES ('1', '同步MySQL數據到 Redis');
克隆,同步MySQL數據到 Redis項目https://github.com/souyunku/YmqExample
$ git clone https://github.com/souyunku/YmqExample.git
打開 ymq-alibaba-otter-canal
項目,運行 SimpleCanalTest
測試類
創建canal客戶端,從canal中獲取數據,並將數據更新至Redis
import com.alibaba.otter.canal.client.CanalConnector; import com.alibaba.otter.canal.client.CanalConnectors; import io.ymq.example.util.AbstractCanalClientTest; import org.apache.commons.lang.exception.ExceptionUtils; import java.net.InetSocketAddress; /** * 單機模式的測試例子 * * @author jianghang 2013-4-15 下午04:19:20 * @version 1.0.4 */ public class SimpleCanalClientTest extends AbstractCanalClientTest { public SimpleCanalClientTest(String destination) { super(destination); } public static void main(String args[]) { // 根據ip,直接建立連接,無HA的功能 String destination = "example"; // String ip = AddressUtils.getHostIp(); CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress("192.168.252.125", 11111), destination, "", ""); final SimpleCanalClientTest clientTest = new SimpleCanalClientTest(destination); clientTest.setConnector(connector); clientTest.start(); Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { try { logger.info("## stop the canal client"); clientTest.stop(); } catch (Throwable e) { logger.warn("##something goes wrong when stopping canal:\n{}", ExceptionUtils.getFullStackTrace(e)); } finally { logger.info("## canal client is down."); } } }); }
UPDATE `penglei`.`test` SET `id`='1', `name`='使用 Alibaba Canal 增量訂閱&消費組件,同步MySQL數據到 Redis' WHERE (`id`='1');
**************************************************** * Batch Id: [27] ,count : [3] , memsize : [325] , Time : 2017-08-29 13:57:33 * Start : [mysql-bin.000005:13948:1503986259000(2017-08-29 13:57:39)] * End : [mysql-bin.000005:14295:1503986259000(2017-08-29 13:57:39)] **************************************************** ================> binlog[mysql-bin.000005:13948] , executeTime : 1503986259000 , delay : -5057ms BEGIN ----> Thread id: 27 ----------------> binlog[mysql-bin.000005:14076] , name[penglei,test] , eventType : UPDATE , executeTime : 1503986259000 , delay : -5057ms id : 1 type=int(11) name : 使用 阿里巴巴 Canal 增量訂閱&消費 binlog 同步 MySQL 數據到 Redis 集羣 type=varchar(1000) update=true -------> before id : 1 type=int(11) name : 使用 阿里巴巴 Canal 增量訂閱&消費 binlog 同步 MySQL 數據到 Redis type=varchar(1000) -------> after ---------------- END ----> transaction id: 307 ================> binlog[mysql-bin.000005:14295] , executeTime : 1503986259000 , delay : -5056ms
查看Redis 是否已經同步
$ /opt/redis-4.0.1/src/redis-cli -h 192.168.252.101 -c -p 6379 192.168.252.104:6379> get ymq-group:1 { "name": "使用 Alibaba Canal 增量訂閱&消費組件,同步MySQL數據到 Redis", "id": "1" }