社交類產(chǎn)品對(duì)搜索功能需求要求就比較高,需要根據(jù)用戶城市,、用戶ID昵稱等進(jìn)行搜索,。
項(xiàng)目原先的搜索接口采用SQL查詢的方式實(shí)現(xiàn),數(shù)據(jù)庫表采用了按城市分表的方式,。但隨著業(yè)務(wù)的發(fā)展,,搜索接口調(diào)用頻次越來越高,搜索接口壓力越來越大,,搜索數(shù)據(jù)庫經(jīng)常崩潰,,從而導(dǎo)致搜索功能經(jīng)常不能使用。
從上面的系統(tǒng)架構(gòu)圖可以看出,,當(dāng)用戶修改資料時(shí),,接口會(huì)修改用戶庫信息,接著觸發(fā)器會(huì)將改變的用戶信息寫入臨時(shí)表,。定時(shí)腳本每隔1分鐘掃描一次臨時(shí)表,,將變更的數(shù)據(jù)寫入到搜索庫中。當(dāng)用戶再次請(qǐng)求搜索接口時(shí),,就可以搜索到最新的數(shù)據(jù),。
從技術(shù)層面分析,原搜索系統(tǒng)的設(shè)計(jì)有以下缺點(diǎn):
搜索信息不實(shí)時(shí),。當(dāng)用戶修改信息時(shí),,需要等待1分鐘的時(shí)間才能將最新的用戶信息同步到搜索數(shù)據(jù)庫中。
ID,、昵稱搜索速度慢,。按照地區(qū)分表的數(shù)據(jù)庫設(shè)計(jì)是為了減輕數(shù)據(jù)庫壓力,保證大部分按照地區(qū)搜索的請(qǐng)求能正常響應(yīng),。但是如果用戶按照ID或昵稱搜索,,那么我們就需要對(duì)成千上萬個(gè)地區(qū)表全都搜索一次,,這時(shí)間復(fù)雜度可想而知。很多時(shí)候按照昵稱和ID搜索速度太慢,,需要10多秒才能響應(yīng),。
系統(tǒng)穩(wěn)定性、拓展性以及處理能力差,。這可以歸結(jié)為技術(shù)老舊,,無法滿足業(yè)務(wù)需求。隨著搜索量的提升,,對(duì)數(shù)據(jù)庫的壓力將會(huì)越來越大,,而MySQL數(shù)據(jù)庫天然不適合用來應(yīng)對(duì)海量的請(qǐng)求。現(xiàn)在已經(jīng)有更加成熟的ElasticSearch可以用來做搜索方面的業(yè)務(wù),。
觸發(fā)器不便于管理,。觸發(fā)器這種東西不好維護(hù),并且擴(kuò)展性很差,,一旦修改的請(qǐng)求變多,,很可能導(dǎo)致整個(gè)數(shù)據(jù)庫崩潰(用戶庫崩潰是很嚴(yán)重的)。
我們總結(jié)一下新搜索系統(tǒng)需要解決的幾個(gè)問題:
海量請(qǐng)求,。幾百萬的請(qǐng)求毫無壓力,,上千萬上億也要可以扛得住。
實(shí)時(shí)搜索,。指的是當(dāng)一個(gè)用戶修改了其數(shù)據(jù)之后,,另一個(gè)用戶能實(shí)時(shí)地搜索到改用戶。
海量請(qǐng)求,。要扛得起海量的搜索請(qǐng)求,,可以使用ElasticSearch來實(shí)現(xiàn),它是在Lucene的基礎(chǔ)上進(jìn)行封裝的一個(gè)開源項(xiàng)目,,它將Lucene復(fù)雜的原理以及API封裝起來,,對(duì)外提供了一個(gè)易用的API接口。ElasticSearch現(xiàn)在已經(jīng)廣泛地被許多公司使用,,其中包括:愛奇藝,、百姓網(wǎng)、58到家等公司,。
實(shí)時(shí)搜索,。阿里有一個(gè)開源項(xiàng)目Canal,就是用來解決這個(gè)問題的,,Canal項(xiàng)目利用了MySQL數(shù)據(jù)庫主從同步的原理,,將Canal Server模擬成一臺(tái)需要同步的從庫,從而讓主庫將binlog日志流發(fā)送到Canal Server接口,。Canal項(xiàng)目對(duì)binlog日志的解析進(jìn)行了封裝,,我們可以直接得到解析后的數(shù)據(jù),,而不需要理會(huì)binlog的日志格式。而且Canal項(xiàng)目整合了zookeeper,,整體實(shí)現(xiàn)了高可用,,可伸縮性強(qiáng),是一個(gè)不錯(cuò)的解決方案,。
經(jīng)過一段時(shí)間的技術(shù)預(yù)研,,我們?cè)O(shè)計(jì)了整個(gè)搜索技術(shù)架構(gòu):
從架構(gòu)圖可以看出整個(gè)系統(tǒng)分為兩大部分:
Canal數(shù)據(jù)變更服務(wù)平臺(tái)。這部分負(fù)責(zé)解析MySQL的binlog日志,,并將其解析后的數(shù)據(jù)封裝成特定的對(duì)象放到Kafka中,。
Kafka數(shù)據(jù)消費(fèi)方,。這部分負(fù)責(zé)消費(fèi)存放在Kafka中的消息,,當(dāng)消費(fèi)方拿到具體的用戶表變更消息時(shí),將最新的用戶信息存放到ES數(shù)據(jù)倉庫中,。
Canal技術(shù)變更基礎(chǔ)平臺(tái)
因?yàn)榭紤]到未來可能有其他項(xiàng)目需要監(jiān)控?cái)?shù)據(jù)庫某些表的變化,,因此我們將Canal獲取MySQL數(shù)據(jù)變更部分做成一個(gè)公用的平臺(tái)。當(dāng)有其他業(yè)務(wù)需要增加監(jiān)控的表時(shí),,我們可以直接修改配置文件,,重啟服務(wù)器即可完成添加,極大地提高了開發(fā)效率,。
在這一部分中,,主要分為兩大部分:Canal Server 和 Canal Client。
Canal Server端,。Canal Server偽裝成MySQL的一個(gè)從庫,,使主庫發(fā)送binlog日志給 Canal Server,Canal Server 收到binlog消息之后進(jìn)行解析,,解析完成后將消息直接發(fā)送給Canal Client,。在Canal Server端可以設(shè)置配置文件進(jìn)行具體scheme(數(shù)據(jù)庫)和table(數(shù)據(jù)庫表)的篩選,從而實(shí)現(xiàn)動(dòng)態(tài)地增加對(duì)數(shù)據(jù)庫表的監(jiān)視,。
Canal Client端,。Canal Client端接收到Canal Server的消息后直接將消息存到Kafka指定Partition中,并將最新的binlogid發(fā)送給zookeeper集群保存,。
Kafka消息消費(fèi)端
Canal技術(shù)變更平臺(tái)在獲取到對(duì)應(yīng)的數(shù)據(jù)庫變更消息后會(huì)將其放到指定的Kafka分片里,,具體的業(yè)務(wù)項(xiàng)目需要到指定的Kafka片區(qū)里消費(fèi)對(duì)應(yīng)的數(shù)據(jù)變更消息,之后根據(jù)具體的業(yè)務(wù)需求進(jìn)行處理,。
因?yàn)镃anal變化是根據(jù)表為最小單位進(jìn)行地,,因此我在實(shí)現(xiàn)方面定義了一個(gè)以表為處理單位的MsgDealer接口:
public interface MsgDealer { void deal(CanalMsgVo canalMsgVo); }
搜索庫涉及對(duì)5個(gè)表的監(jiān)視,因此我實(shí)現(xiàn)了5個(gè)對(duì)應(yīng)的處理類:
針對(duì)不同表的數(shù)據(jù)變化,,自動(dòng)調(diào)用不同的實(shí)現(xiàn)類進(jìn)行處理,。
|