方案一:(同步操作,代碼侵入性比較高)
在往數據庫中增加一條數據的同時,向es中也插入一條
Books.objects.create()
向es中插入一條數據
方案二:使用celery起一個定時任務
在用戶低峰的時候,執行定時任務(比如每天晚上00:00),把當天新增的數據查詢出來並處理成Json格式存到es中。
向es中插入一條
方案三:使用django信號
-Books.objects.create()
在插入后觸發某個信號(post_save) # 具體看django內置信號總結
方案四:單獨做一個服務做同步
在github里面有個用go語言寫的開源模塊go-mysql-elasticsearch,下載地址:https://github.com/siddontang/go-mysql-elasticsearch
go mysql elasticsearch是一項將mysql數據自動同步到elasticsearch的服務。它首先使用mysqldump獲取源數據,然后用binlog增量同步數據。
Golang 支持交叉編譯,在一個平台上生成另一個平台的可執行程序
# 1. Mac 下編譯 Linux 和 Windows 64位可執行程序
CGO_ENABLED=0
GOOS=linux
GOARCH=amd64
go build main.go
CGO_ENABLED=0
GOOS=windows
GOARCH=amd64
go build main.go
//CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build
# 2. Linux 下編譯 Mac 和 Windows 64位可執行程序
CGO_ENABLED=0
GOOS=darwin
GOARCH=amd64
go build main.go
CGO_ENABLED=0
GOOS=windows
GOARCH=amd64
go build main.go
# 3. Windows 下編譯 Mac 和 Linux 64位可執行程序
SET CGO_ENABLED=0
SET GOOS=darwin
SET GOARCH=amd64
go build main.go
SET CGO_ENABLED=0
SET GOOS=linux
SET GOARCH=amd64
go build main.go
在加入 PingCAP 之前,很長一段時間,我都跟 MySQL 打交道。MySQL 性能強悍,但是在一些全文檢索,復雜查詢上面並不快,效率堪憂。為了解決快速查的問題,我們之前嘗試考慮過 Sphinx,但總覺得使用起來不方便。恰好那時候碰到了 Elasticsearch(ES),立刻就覺得這特么就是我們要的東西。
ES 底層基於 Lucene ,支持分布式,同時還提供了強大的 web 頁面,點點鼠標就很容易進行數據查詢。但我們的數據是存放在 MySQL 里面,如何將數據實時的導入給 Elasticsearch?
最開始我想到的是用 MySQL trigger 或者 UDF,不過立刻覺得這條路非常不靠譜,所以就把目標放在了 MySQL binlog 上面。
1.MySQL Binlog
要通過 MySQL binlog 將 MySQL 的數據同步給 ES, 我們只能使用 row 模式的 binlog。如果使用 statement 或者 mixed format,我們在 binlog 里面只能知道對應的 query 語句,完全沒法知道這條語句到底改了啥數據,所以要從 binlog 里面得到實際的數據,只能用 row 模式。
Row 模式還可以設置 full,noblob 以及 minimal 三種 image 模式,后面兩種主要是為了減少空間占用,默認是 full。個人其實最喜歡 full 模式,這樣數據最全,而且也覺得空間占用對於現在的硬盤來說不是特別大的問題,畢竟我們還有定期清理 binlog 的機制。
同步 MySQL binlog 就很簡單了,按照 MySQL replication 的協議,自己寫一個客戶端,模擬成 MySQL slave,注冊給 MySQL master 就可以了。MySQL master 會實時的將數據的更新通過 binlog event 發送給 slave,然后我們自己解析 event 之后就能得到實際的數據了。
具體實現這里不做過多說明,大家可以參考 MySQL Client/Server Protocol 細致的了解 MySQL 的 protocol,binlog events 等相關知識。我也在 go-mysql項目里面實現了相關的 replication功能。
2.MySQL dump
如果是一個新建 MySQL,我們當然可以通過 binlog 的方式方便的同步數據。但如果我們想同步一個已經運行一段時間的 MySQL ,就可能會有問題了。因為這時候早期的 binlog 文件已經被刪除,如果直接開始同步,我們就可能會缺失一部分早期更新的數據。
要解決這個辦法也比較容易,參考 MySQL 通用的 backup方式,我們可以先使用 mysqldump 獲取當前 MySQL 的整個 snapshot,直接解析生成的 dump 文件,就能得到當前所有的數據。然后在從這個 snapshot 對應的 binlog position 位置開始同步。
整個這套流程我也在 go-mysql 的 canal里面實現。
3.go-mysql-elasticsearch
我們通過 go-mysql 的 canal 組件,能非常方便的同步 MySQL 的數據,那么剩下的事情就簡單了,將同步的 MySQL 數據直接發送給 ES 就可以了。
因為 ES 對外提供的 API 非常簡單易用,我們非常容易就能寫一個 client 與其交互,代碼在 這里,不做詳細說明了。
我們唯一需要注意的就是設置同步規則,也就是說,當我們同步了一行數據之后,這一行數據到底是如何組裝發送給 ES 的。
同步規則在配置文件里面詳細說明。首先,我們要定義需要同步的 table,這個我們在 source 里面指定,例如:
[[source]]
schema = "test"
# Only below tables will be synced into Elasticsearch.
# "t_[0-9]{4}" is a wildcard table format, you can use it if you have many sub tables, like table_0000 - table_1023
tables = ["t", "t_[0-9]{4}", "tfield", "tfilter"]
在上面的例子中,我們需要同步 test 這個 database 里面的幾張表。對於一些項目如果使用了分表機制,我們可以用通配符來匹配,譬如上面的 t_[0-9]{4}
,就可以匹配 table t_0000
到 t_9999
。
對一個 table,我們需要指定將它的數據同步到 ES 的哪一個 index 的 type 里面。如果不指定,我們默認會用起 schema name 作為 ES 的 index 和 type。例如:
[[rule]]
schema = "test"
table = "t"
index = "test"
type = "t"
在上面的例子中,我們將 table t
的數據同步到 ES 的 index 為 test
, type 為 t
的下面。
4. 自定義 Field mapping
默認情況下面,我們會使用 table 的 column name 作為 ES 里面的 field name,但有時候我們也可以換成另外的名字,例如:
[[rule]]
schema = "test"
table = "tfield"
index = "test"
type = "tfield"
[rule.field]
# Map column `id` to ES field `es_id`
id="es_id"
# Map column `tags` to ES field `es_tags` with array type
tags="es_tags,list"
# Map column `keywords` to ES with array type
keywords=",list"
在上面的例子中,table tfield
的 column id
,我們映射成了 es_id
,而 tags
則映射成了 es_tags
。
上面我們可以注意到 list
這個字段,他顯示的告知需要將對應的 column 數據轉成 ES 的 array type。這個現在通常用於 MySQL 的 varchar 等類型,我們可能會存放類似 “a,b,c” 這樣的數據,然后希望同步給 ES 的時候變成 [a, b, c]
這樣的列表形式。
5.Filter Field
[[rule]]
schema = "test"
table = "tfilter"
index = "test"
type = "tfilter"
# Only sync following columns
filter = ["id", "name"]
上面對於 table tfilter
,我們只會同步 id
和 name
這兩列,其他的都不會同步了。
6.聚合多張表
上面我們說了支持多張表聚合,譬如:
[[rule]]
schema = "test"
table = "t_[0-9]{4}"
index = "test"
type = "t"
在上面的例子中,我們會將所有滿足格式 t_[0-9]{4}
的 table 同步到 ES 的 index 為 test
,type 為 t
的下面。當然,這些表需要保證 schema 是一致的。
7.小結
可以看到,使用 go-mysql-elasticsearch,我們僅需要在配置文件里面寫規則,就能非常方便的將數據從 MySQL 同步給 ES。上面僅僅舉了一些簡單的例子,go-mysql-elasticserch 現在還支持 parent-child relationship 的同步等。
當然,go-mysql-elasticsearch 還不完善,譬如還不能很好的處理 DDL 的情況,還需要支持更多的同步規則等。如果大家有興趣,非常歡迎參與進來。