TiDB Hackathon 2018 共評選出六組優秀項目,本系列文章將由這六組項目的成員主筆,分享他們的參賽經驗和成果。咱們很是但願本屆 Hackathon 誕生的優秀項目可以在社區中延續下去,感興趣的小夥伴們能夠加入進來哦~本文做者是來自 DSG 團隊的楊文同窗,他們的項目《天真貝葉斯學習機》在本屆 Hackathon 中得到了三等獎+最佳創意獎。mysql
「在 TiDB Hackathon 2018 學習到很多東西,但願明年再來」git
「pd ctl 天真學習機」github
具體作法:用 naive bayes 模型來根據系統指標和人的 pd ctl 調用,來獲得一個模型去根據系統指標去自動提供 pd ctl 調用的命令。算法
貝葉斯模型能夠用來幹這種事:sql
好比一個媽媽根據天氣預報來跟兒子在出們的時候叮囑:json
天氣預報[ 晴, 溫度: 28, 風力: 中 ], 媽媽會說 [好好玩] 天氣預報[ 雨, 溫度: 15, 風力: 低 ], 媽媽會說 [帶上傘] 天氣預報[ 陰, 溫度: 02, 風力: 大 ], 媽媽會說 [多穿點]...
把這些輸入輸入到貝葉斯模型裏之後, 模型能夠根據天氣預報來輸出:bootstrap
天氣預報[ 晴, 溫度: 00, 風力中], 模型會說 [ 多穿點:0.7, 好好玩0.2, 帶上傘0.1] 天氣預報[ 雨, 溫度: 10, 風力大], 模型會說 [ 帶上傘:0.8, 多穿點0.1, 好好玩0.1]
這樣經過一個媽媽的叮囑就能夠訓練出一個也會根據天氣預報給出叮囑的模型。服務器
咱們能夠把一個模型單獨的部署在一個 pod 裏, 暴露一個 service ,而後集羣上每次有人去調用 pd_ctl 的時候就在後臺用 rest call 到模型服務上記錄一下操做(叮囑)和當前的系統指標(比如天氣預報). 這樣慢慢用一段時間之後,積累的操做多了之後,就能夠打開某個自動響應,或者打開自動建議應該執行的命令的功能。網絡
這樣模型能夠在某一組系統指標出現以前相似學習過的狀態以後,給出相應的建議,當這些建議都很正確的時候直接讓 pd 直接採納,徹底智能的自動化運做。ide
在跟導師交流探討後發現,目前 PD 已經比較自動化了,不多須要人爲介入進行操做,須要的時候也是比較複雜的場景,或者自動化運做比較慢的場景。
咱們團隊在跟多名導師的溝通交流下,將初步想法進行了一些調整:
首先,介紹一下咱們團隊(DSG),分別來自:丹麥、北京(山西)、廣州。
D 先生是在比賽前一天早上到達北京的,我是比賽前一天晚上從廣州出發,於比賽當日早上 6:38 才抵達北京的。
說實話,時差和疲憊對於參賽仍是有一點影響的。
廢話很少說,我就來回顧一下個人整個參勝過程。
徹底參考文檔
測試 TiDB 集羣,可能遇到的坑(MySQL 8 client On MacOSX):
mysql -hx.x.x.x --default-character-set utf8
/model/service1
PUT 上報數據:{ "updates": [ [ "transfer leader from store 7 to store 2", [ { "feature_type": "Category", "name": "hotRegionsCount1", "value": "true" }, { "feature_type": "Category", "name": "minRegionsCount1", "value": "true" }, { "feature_type": "Category", "name": "hotRegionsCount2", "value": "true" }, { "feature_type": "Category", "name": "minRegionsCount2", "value": "true" }, { "feature_type": "Category", "name": "srcRegion", "value": "7" } ] ], ]}
/model/service1
POST 獲取模型結果:輸入參數:上報的參數
{ "predictions": [ { "transfer leader from store 1 to store 2": 0.27432775221072137, "transfer leader from store 1 to store 7": 0.6209064350448428, "transfer leader from store 2 to store 1": 0.024587894827775753, "transfer leader from store 2 to store 7": 0.01862719305134528, "transfer leader from store 7 to store 1": 0.02591609468013258, "transfer leader from store 7 to store 2": 0.03563463018518229 } ]}
首先將 pd-server 替換到集羣所在 ansible/resources/bin
目錄下,那如何讓集羣上的 PD 更新生效呢?
更新:
$ ansible-playbook rolling_update.yml --tags=pd
在實操過程當中, 若是你在更新到一半的時候就關門了,可能會致使整個 PD 掛掉(非集羣環境),多是由於邏輯不嚴謹所致使的問題
直接中止了 ansible,致使 PD 集羣機器節點有中止的狀況,這個時候你能夠經過如下命令啓動它。
啓動:
$ ansible-playbook start.yml --tags=pd
你們都覺得能夠經過配置來解決:(調度開關方法: 用 config set xxx 0 來關閉調度)
配置以下:(雖然找的地方錯誤了,可是錯打錯着,咱們來到了 Demo Time:
config set leader-schedule-limit 0 config set region-schedule-limit 0 scheduler add hot-region-scheduler config show config set leader-schedule-limit 4 config set region-schedule-limit 8
實測發現,根本不生效,必需要改源代碼。
func (h *balanceHotRegionsScheduler) dispatch(typ BalanceType, cluster schedule.Cluster) []*schedule.Operator { h.Lock() defer h.Unlock() switch typ { case hotReadRegionBalance: h.stats.readStatAsLeader = h.calcScore(cluster.RegionReadStats(), cluster, core.LeaderKind) // return h.balanceHotReadRegions(cluster) // 將這一行註釋 case hotWriteRegionBalance: h.stats.writeStatAsLeader = h.calcScore(cluster.RegionWriteStats(), cluster, core.LeaderKind) h.stats.writeStatAsPeer = h.calcScore(cluster.RegionWriteStats(), cluster, core.RegionKind) // return h.balanceHotWriteRegions(cluster) // 將這一行註釋 } return nil }
可是,咱們要的不是不調度,而只是不給調度結果:
func (h *balanceHotRegionsScheduler) balanceHotReadRegions(cluster schedule.Cluster) []*schedule.Operator { // balance by leader srcRegion, newLeader := h.balanceByLeader(cluster, h.stats.readStatAsLeader) if srcRegion != nil { schedulerCounter.WithLabelValues(h.GetName(), "move_leader").Inc() // step := schedule.TransferLeader{FromStore: srcRegion.GetLeader().GetStoreId(), ToStore: newLeader.GetStoreId()} // 修改成不返回值或者返回 _ _ = schedule.TransferLeader{FromStore: srcRegion.GetLeader().GetStoreId(), ToStore: newLeader.GetStoreId()} // return []*schedule.Operator{schedule.NewOperator("transferHotReadLeader", srcRegion.GetID(), srcRegion.GetRegionEpoch(), schedule.OpHotRegion|schedule.OpLeader, step)} // 註釋這一行,並 return nil return nil } // balance by peer srcRegion, srcPeer, destPeer := h.balanceByPeer(cluster, h.stats.readStatAsLeader) if srcRegion != nil { schedulerCounter.WithLabelValues(h.GetName(), "move_peer").Inc() return []*schedule.Operator{schedule.CreateMovePeerOperator("moveHotReadRegion", cluster, srcRegion, schedule.OpHotRegion, srcPeer.GetStoreId(), destPeer.GetStoreId(), destPeer.GetId())} } schedulerCounter.WithLabelValues(h.GetName(), "skip").Inc() return nil } ...... func (h *balanceHotRegionsScheduler) balanceHotWriteRegions(cluster schedule.Cluster) []*schedule.Operator { for i := 0; i < balanceHotRetryLimit; i++ { switch h.r.Int() % 2 { case 0: // balance by peer srcRegion, srcPeer, destPeer := h.balanceByPeer(cluster, h.stats.writeStatAsPeer) if srcRegion != nil { schedulerCounter.WithLabelValues(h.GetName(), "move_peer").Inc() fmt.Println(srcRegion, srcPeer, destPeer) // return []*schedule.Operator{schedule.CreateMovePeerOperator("moveHotWriteRegion", cluster, srcRegion, schedule.OpHotRegion, srcPeer.GetStoreId(), destPeer.GetStoreId(), destPeer.GetId())} // 註釋這一行,並 return nil return nil } case 1: // balance by leader srcRegion, newLeader := h.balanceByLeader(cluster, h.stats.writeStatAsLeader) if srcRegion != nil { schedulerCounter.WithLabelValues(h.GetName(), "move_leader").Inc() // step := schedule.TransferLeader{FromStore: srcRegion.GetLeader().GetStoreId(), ToStore: newLeader.GetStoreId()} // 修改成不返回值或者返回 _ _ = schedule.TransferLeader{FromStore: srcRegion.GetLeader().GetStoreId(), ToStore: newLeader.GetStoreId()} // return []*schedule.Operator{schedule.NewOperator("transferHotWriteLeader", srcRegion.GetID(), srcRegion.GetRegionEpoch(), schedule.OpHotRegion|schedule.OpLeader, step)} // 註釋這一行,並 return nil return nil } } } schedulerCounter.WithLabelValues(h.GetName(), "skip").Inc() return nil }
當修改了 PD 再從新編譯獲得 pd-server,將其放到
tidb-ansible/resources/bin/pd-server
並替換原來的文件,而後執行
ansible-playbook rolling_update.yml --tags=pd
,便可重啓 pd-server 服務。
在調優的過程當中發現,當前 hot-region-scheduler
的調度時對於目標機器的選擇並非最優的,代碼以下:
https://github.com/pingcap/pd/blob/master/server/schedulers/hot_region.go#L374
簡述:循環遍歷 candidateStoreIDs 的時候,若是知足條件有多臺,那麼最後一個總會覆蓋前面已經存儲到 destStoreID 裏面的數據,最終咱們拿到的 destStoreID 有可能不是最優的。
// selectDestStore selects a target store to hold the region of the source region. // We choose a target store based on the hot region number and flow bytes of this store. func (h *balanceHotRegionsScheduler) selectDestStore(candidateStoreIDs []uint64, regionFlowBytes uint64, srcStoreID uint64, storesStat core.StoreHotRegionsStat) (destStoreID uint64) { sr := storesStat[srcStoreID] srcFlowBytes := sr.TotalFlowBytes srcHotRegionsCount := sr.RegionsStat.Len() var ( minFlowBytes uint64 = math.MaxUint64 minRegionsCount = int(math.MaxInt32) ) for _, storeID := range candidateStoreIDs { if s, ok := storesStat[storeID]; ok { if srcHotRegionsCount-s.RegionsStat.Len() > 1 && minRegionsCount > s.RegionsStat.Len() { destStoreID = storeID minFlowBytes = s.TotalFlowBytes minRegionsCount = s.RegionsStat.Len() continue // 這裏 } if minRegionsCount == s.RegionsStat.Len() && minFlowBytes > s.TotalFlowBytes && uint64(float64(srcFlowBytes)*hotRegionScheduleFactor) > s.TotalFlowBytes+2*regionFlowBytes { minFlowBytes = s.TotalFlowBytes destStoreID = storeID } } else { destStoreID = storeID return } } return }
本次咱們只 hack 驗證了 Write Region Leader 這部分,因此咱們重點關注一下監控和問題:
監控數據有必定的延時(粗略估計1-2分鐘)
修改 tidb-bench 的 Makefile#load
模塊對應的主機地址,而後執行 make tbl, make load
便可往服務器 load 數據了。
注意,這裏你也須要進行一些配置修改:--default-character-set utf8
犯的錯:受限於本地-服務器間網絡帶寬,導入數據很慢。
$ ./go-ycsb run mysql -p mysql.host=10.9.x.x -p mysql.port=4000 -p mysql.db=test1 -P workloads/workloada
注:go-ycsb
支持 insert,也支持 update,你能夠根據你的須要進行相對應的調整 workloada#recordcount
和 workloada#operationcount
參數。
rust 一天速成……
Demo Time 的時候聽好幾個團隊都說失敗了。我之前也嘗試過,可是被編譯的速度以及耗能給擊敗了。
環境均可以把你 de 自信心擊潰。
rustup install nightly cargo run ...
Mac 本地打包 Linux 失敗:缺乏 std 庫,經過 Docker 臨時解決。
從比賽一開始,導師團就很是積極和主動,直接去每一個項目組,給予直接指導和建議,咱們遇到問題去找導師時,他們也很是的配合。
導師不只幫咱們解決問題(特別是熱點數據構建,包括對於代碼級別的指導),還跟咱們一塊兒探討課題方向和實際可操做性,以及能夠達到的目標。
很是感謝!!!
咱們的準備和主動性真的不足,值得反思--也但願你們之後不要怕麻煩,有問題就大膽的去問。
整個 Demo show 進行的很是順利,爲每個團隊點贊!
不少團隊的做品都讓人尖叫,可想而知他們的做品是多麼的酷炫和牛逼,印象中只有一個團隊在 Demo 環境出現了演示時程序崩潰的問題(用Java Netty 基於 TiKV 作的 memcache(實現了大部分的協議))。
遺憾!!!
咱們 DSG 團隊榮獲三等獎+最佳創意兩項大獎,可是很遺憾我未能跟團隊一塊兒分享這一刻。
由於我要趕着去火車站,因此在週日下午6點的時候,我跟隊友和一些朋友作別後,我就去火車站了,後面幾組的 Demo Show 也很很是遺憾未能參加。
得獎感言:
謝謝 DSG 團隊,謝謝導師,謝謝評委老師,謝謝 PingCAP 給你們籌備了這麼好的一次黑客馬拉松比賽活動。
本次比賽的各個方面都作的完美,除了網絡。
ansible-playbook bootstrap.yml --extra-vars "dev_mode=True"
ansible-playbook unsafe_cleanup_data.yml
(https://github.com/pingcap/docs/blob/master/op-guide/ansible-operation.md)
後續楊文同窗會在 我的博客 中更新更多項目細節。