<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    在技術(shù)方面,我自己熱衷于 Open Source,寫了很多 Open Source 的東西,擅長的是 Infrastructure 領(lǐng)域。Infrastructure 領(lǐng)域現(xiàn)在范圍很廣,比如說很典型的分布式 Scheduler、Mesos、Kubernetes,另外它和 Microservices 所結(jié)合的東西也特別多。Infrastructure 領(lǐng)域還有比如 Database 有分 AP(分析型)和 TP(事務(wù)型),比如說很典型的大家知道的 Spark、Greenplum、Apache Phoenix 等等,這些都屬于在 AP 的,它們也會去嘗試支持有限的 TP。另外,還有一個(gè)比較有意思的就是 Kudu——Cloudera Open Source 的那個(gè)項(xiàng)目,它的目標(biāo)很有意思:我不做最強(qiáng)的 AP 系統(tǒng),也不做最強(qiáng)的 TP 系統(tǒng),我選擇一個(gè)相對折中的方案。從文化哲學(xué)上看,它比較符合中國的中庸思想。

    另外,我先后創(chuàng)建了 Codis、TiDB。去年12月份創(chuàng)建了 TiKV 這個(gè) project,TiKV 在所有的 rust 項(xiàng)目里目前排名前三。

    首先我們聊聊 Database 的歷史,在已經(jīng)有這么多種數(shù)據(jù)庫的背景下我們?yōu)槭裁匆獎(jiǎng)?chuàng)建另外一個(gè)數(shù)據(jù)庫;以及說一下現(xiàn)在方案遇到的困境,說一下 Google Spanner 和 F1、TiKV 和 TiDB,說一下架構(gòu)的事情,在這里我們會重點(diǎn)聊一下 TiKV。因?yàn)槲覀儺a(chǎn)品的很多特性是 TiKV 提供的,比如說跨數(shù)據(jù)中心的復(fù)制、Transaction、auto-scale。

    接下來聊一下為什么 TiKV 用 Raft 能實(shí)現(xiàn)所有這些重要的特性,以及 scale、MVCC 和事務(wù)模型。東西非常多,我今天不太可能把里面的技術(shù)細(xì)節(jié)都描述得特別細(xì),因?yàn)閹缀趺恳粋€(gè)話題都可以找到一篇或者是多篇論文,所以詳細(xì)的技術(shù)問題大家可以單獨(dú)來找我聊。

    后面再說一下我們現(xiàn)在遇到的窘境,就是大家常規(guī)遇到的分布式方案有哪些問題,比如 MySQL Sharding。我們創(chuàng)建了無數(shù) MySQL Proxy,比如官方的 MySQL proxy、Youtube 的 Vitess、淘寶的 Cobar、TDDL以及基于 Cobar 的 MyCAT、金山的 Kingshard、360 的 Atlas、京東的 JProxy,我在豌豆莢也寫了一個(gè)。可以說,隨便一個(gè)大公司都會造一個(gè) MySQL Sharding 的方案。

    為什么我們要?jiǎng)?chuàng)建另外一個(gè)數(shù)據(jù)庫?

    昨天晚上我還跟一個(gè)同學(xué)聊到,基于 MySQL 的方案它的天花板在哪里,它的天花板特別明顯。有一個(gè)思路是能不能通過 MySQL 的 server 把 InnoDB 變成一個(gè)分布式數(shù)據(jù)庫,聽起來這個(gè)方案很完美,但是很快就會遇到天花板。因?yàn)?MySQL 生成的執(zhí)行計(jì)劃是個(gè)單機(jī)的,它認(rèn)為整個(gè)計(jì)劃的 cost 也是單機(jī)的,我讀取一行和讀取下一行之間的開銷是很小的,比如迭代 next row 可以立刻拿到下一行。實(shí)際上在一個(gè)分布式系統(tǒng)里面,這是不一定的。

    另外,你把數(shù)據(jù)都拿回來計(jì)算這個(gè)太慢了,很多時(shí)候我們需要把我們的 expression 或者計(jì)算過程等等運(yùn)算推下去,向上返回一個(gè)最終的計(jì)算結(jié)果,這個(gè)一定要用分布式的 plan,前面控制執(zhí)行計(jì)劃的節(jié)點(diǎn),它必須要理解下面是分布式的東西,才能生成最好的 plan,這樣才能實(shí)現(xiàn)最高的執(zhí)行效率。

    比如說你做一個(gè) sum,你是一條條拿回來加,還是讓一堆機(jī)器一起算,最后給我一個(gè)結(jié)果。 例如我有 100 億條數(shù)據(jù)分布在 10 臺機(jī)器上,并行在這 10臺機(jī)器我可能只拿到 10 個(gè)結(jié)果,如果把所有的數(shù)據(jù)每一條都拿回來,這就太慢了,完全喪失了分布式的價(jià)值。聊到 MySQL 想實(shí)現(xiàn)分布式,另外一個(gè)實(shí)現(xiàn)分布式的方案就是 Proxy。但是 Proxy 本身的天花板在那里,就是它不支持分布式的 transaction,它不支持跨節(jié)點(diǎn)的 join,它無法理解復(fù)雜的 plan,一個(gè)復(fù)雜的 plan 打到 Proxy 上面,Proxy 就傻了,我到底應(yīng)該往哪一個(gè)節(jié)點(diǎn)上轉(zhuǎn)發(fā)呢,如果我涉及到 subquery sql 怎么辦?所以這個(gè)天花板是瞬間會到,在傳統(tǒng)模型下面的修改,很快會達(dá)不到我們的要求。

    另外一個(gè)很重要的是,MySQL 支持的復(fù)制方式是半同步或者是異步,但是半同步可以降級成異步,也就是說任何時(shí)候數(shù)據(jù)出了問題你不敢切換,因?yàn)橛锌赡苁钱惒綇?fù)制,有一部分?jǐn)?shù)據(jù)還沒有同步過來,這時(shí)候切換數(shù)據(jù)就不一致了。前一陣子出現(xiàn)過某公司突然不能支付了這種事件,今年有很多這種類似的 case,所以微博上大家都在說“說好的異地多活呢?”……

    為什么傳統(tǒng)的方案在這上面解決起來特別的困難,天花板馬上到了,基本上不可能解決這個(gè)問題。另外是多數(shù)據(jù)中心的復(fù)制和數(shù)據(jù)中心的容災(zāi),MySQL 在這上面是做不好的。

    在前面三十年基本上是關(guān)系數(shù)據(jù)庫的時(shí)代,那個(gè)時(shí)代創(chuàng)建了很多偉大的公司,比如說 IBM、Oracle、微軟也有自己的數(shù)據(jù)庫,早期還有一個(gè)公司叫 Sybase,有一部分特別老的程序員同學(xué)在當(dāng)年的教程里面還可以找到這些東西,但是現(xiàn)在基本上看不到了。

    另外是 NoSQL。NoSQL 也是一度非常火,像 Cassandra、MongoDB 等等,這些都屬于在互聯(lián)網(wǎng)快速發(fā)展的時(shí)候創(chuàng)建這些能夠 scale 的方案,但 Redis scale 出來比較晚,所以很多時(shí)候大家把 Redis 當(dāng)成一個(gè) Cache,現(xiàn)在慢慢大家把它當(dāng)成存儲不那么重要的數(shù)據(jù)的數(shù)據(jù)庫。因?yàn)樗辛?scale 支持以后,大家會把更多的數(shù)據(jù)放在里面。

    然后到了 2015,嚴(yán)格來講是到 2014 年到 2015 年之間,Raft 論文發(fā)表以后,真正的 NewSQL 的理論基礎(chǔ)終于完成了。我覺得 NewSQL 這個(gè)理論基礎(chǔ),最重要的劃時(shí)代的幾篇論文,一個(gè)是谷歌的 Spanner,是在 2013 年初發(fā)布的;再就是 Raft 是在 2014 年上半年發(fā)布的。這幾篇相當(dāng)于打下了分布式數(shù)據(jù)庫 NewSQL 的理論基礎(chǔ),這個(gè)模型是非常重要的,如果沒有模型在上面是堆不起來東西的。說到現(xiàn)在,大家可能對于模型還是可以理解的,但是對于它的實(shí)現(xiàn)難度很難想象。

    前面我大概提到了我們?yōu)槭裁葱枰硗庖粋€(gè)數(shù)據(jù)庫,說到 Scalability 數(shù)據(jù)的伸縮,然后我們講到需要 SQL,比如你給我一個(gè)純粹的 key-velue 系統(tǒng)的 API,比如我要查找年齡在 10 歲到 20 歲之間的 email 要滿足一個(gè)什么要求的。如果只有 KV 的 API 這是會寫死人的,要寫很多代碼,但是實(shí)際上用 SQL 寫一句話就可以了,而且 SQL 的優(yōu)化器對整個(gè)數(shù)據(jù)的分布是知道的,它可以很快理解你這個(gè) SQL,然后會得到一個(gè)最優(yōu)的 plan,他得到這個(gè)最優(yōu)的 plan 基本上等價(jià)于一個(gè)真正理解 KV 每一步操作的人寫出來的程序。通常情況下,SQL 的優(yōu)化器是為了更加了解或者做出更好的選擇。

    另外一個(gè)就是 ACID 的事務(wù),這是傳統(tǒng)數(shù)據(jù)庫必須要提供的基礎(chǔ)。以前你不提供 ACID 就不能叫數(shù)據(jù)庫,但是近些年大家寫一個(gè)內(nèi)存的 map 也可以叫自己是數(shù)據(jù)庫。大家寫一個(gè) append-only 文件,我們也可以叫只讀數(shù)據(jù)庫,數(shù)據(jù)庫的概念比以前極大的泛化了。

    另外就是高可用和自動(dòng)恢復(fù),他們的概念是什么呢?有些人會有一些誤解,因?yàn)榻裉爝€有朋友在現(xiàn)場問到,出了故障,比如說一個(gè)機(jī)房掛掉以后我應(yīng)該怎么做切換,怎么操作。這個(gè)實(shí)際上相當(dāng)于還是上一代的概念,還需要人去干預(yù),這種不算是高可用。

    未來的高可用一定是系統(tǒng)出了問題馬上可以自動(dòng)恢復(fù),馬上可以變成可用。比如說一個(gè)機(jī)房掛掉了,十秒鐘不能支付,十秒鐘之后系統(tǒng)自動(dòng)恢復(fù)了變得可以支付,即使這個(gè)數(shù)據(jù)中心再也不起來我整個(gè)系統(tǒng)仍然是可以支付的。Auto-Failover 的重要性就在這里。大家不希望在睡覺的時(shí)候被一個(gè)報(bào)警給拉起來,我相信大家以后具備這樣一個(gè)能力,5 分鐘以內(nèi)的報(bào)警不用理會,掛掉一個(gè)機(jī)房,又掛掉一個(gè)機(jī)房,這種連續(xù)報(bào)警才會理。我們內(nèi)部開玩笑說,希望大家都能睡個(gè)好覺,很重要的事情就是這個(gè)。

    說完應(yīng)用層的事情,現(xiàn)在很有很多業(yè)務(wù),在應(yīng)用層自己去分片,比如說我按照 user ID在代碼里面分片,還有一部分是更高級一點(diǎn)我會用到一致性哈希。問題在于它的復(fù)雜度,到一定程度之后我自動(dòng)的分庫,自動(dòng)的分表,我覺得下一代數(shù)據(jù)庫是不需要理解這些東西的,不需要了解什么叫做分庫,不需要了解什么叫做分表,因?yàn)橄到y(tǒng)是全部自動(dòng)搞定的。同時(shí)復(fù)雜度,如果一個(gè)應(yīng)用不支持事務(wù),那么在應(yīng)用層去做,通常的做法是引入一個(gè)外部隊(duì)列,引入大量的程序機(jī)制和狀態(tài)轉(zhuǎn)換,A 狀態(tài)的時(shí)候允許轉(zhuǎn)換到 B 狀態(tài),B 狀態(tài)允許轉(zhuǎn)換到 C 狀態(tài)。

    舉一個(gè)簡單的例子,比如說在京東上買東西,先下訂單,支付狀態(tài)之后這個(gè)商品才能出庫,如果不是支付狀態(tài)一定不能出庫,每一步都有嚴(yán)格的流程。

    Google Spanner / F1

    說一下 Google 的 Spanner 和 F1,這是我非常喜歡的論文,也是我最近幾年看過很多遍的論文。 Google Spanner 已經(jīng)強(qiáng)大到什么程度呢?Google Spanner 是全球分布的數(shù)據(jù)庫,在國內(nèi)目前普遍做法叫做同城兩地三中心,它們的差別是什么呢?以 Google 的數(shù)據(jù)來講,谷歌比較高的級別是他們有 7 個(gè)副本,通常是美國保存 3 個(gè)副本,再在另外 2 個(gè)國家可以保存 2 個(gè)副本,這樣的好處是萬一美國兩個(gè)數(shù)據(jù)中心出了問題,那整個(gè)系統(tǒng)還能繼續(xù)可用,這個(gè)概念就是比如美國 3 個(gè)副本全掛了,整個(gè)數(shù)據(jù)都還在,這個(gè)數(shù)據(jù)安全級別比很多國家的安全級別還要高,這是 Google 目前做到的,這是全球分布的好處。

    現(xiàn)在國內(nèi)主流的做法是兩地三中心,但現(xiàn)在基本上都不能自動(dòng)切換。大家可以看到很多號稱實(shí)現(xiàn)了兩地三中心或者異地多活,但是一出現(xiàn)問題都說不好意思這段時(shí)間我不能提供服務(wù)了。大家無數(shù)次的見到這種 case, 我就不列舉了。

    Spanner 現(xiàn)在也提供一部分 SQL 特性。在以前,大部分 SQL 特性是在 F1 里面提供的,現(xiàn)在 Spanner 也在逐步豐富它的功能,Google 是全球第一個(gè)做到這個(gè)規(guī)模或者是做到這個(gè)級別的數(shù)據(jù)庫。事務(wù)支持里面 Google 有點(diǎn)黑科技(其實(shí)也沒有那么黑),就是它有GPS 時(shí)鐘和原子鐘。大家知道在分布式系統(tǒng)里面,比如說數(shù)千臺機(jī)器,兩個(gè)事務(wù)啟動(dòng)先后順序,這個(gè)順序怎么界定(事務(wù)外部一致性)。這個(gè)時(shí)候 Google 內(nèi)部使用了 GPS 時(shí)鐘和原子鐘,正常情況下它會使用一個(gè)GPS 時(shí)鐘的一個(gè)集群,就是說我拿的一個(gè)時(shí)間戳,并不是從一個(gè) GPS 上來拿的時(shí)間戳,因?yàn)榇蠹抑浪械挠布紩姓`差。如果這時(shí)候我從一個(gè)上拿到的 GPS 本身有點(diǎn)問題,那么你拿到的這個(gè)時(shí)鐘是不精確的。而 Google 它實(shí)際上是在一批 GPS 時(shí)鐘上去拿了能夠滿足 majority 的精度,再用時(shí)間的算法,得到一個(gè)比較精確的時(shí)間。大家知道 GPS 也不太安全,因?yàn)樗敲绹姺降模瑢τ?Google 來講要實(shí)現(xiàn)比國家安全級別更高的數(shù)據(jù)庫,而 GPS 是可能受到干擾的,因?yàn)?GPS 信號是可以調(diào)整的,這在軍事用途上面很典型的,大家知道導(dǎo)彈的制導(dǎo)需要依賴 GPS,如果調(diào)整了 GPS 精度,那么導(dǎo)彈精度就廢了。所以他們還用原子鐘去校正 GPS,如果 GPS 突然跳躍了,原子鐘上是可以檢測到 GPS 跳躍的,這部分相對有一點(diǎn)黑科技,但是從原理上來講還是比較簡單,比較好理解的。

    最開始它 Spanner 最大的用戶就是 Google 的 Adwords,這是 Google 最賺錢的業(yè)務(wù),Google 就是靠廣告生存的,我們一直覺得 Google 是科技公司,但是他的錢是從廣告那來的,所以一定程度來講 Google 是一個(gè)廣告公司。Google 內(nèi)部的方向先有了 Big table ,然后有了 MegaStore ,MegaStore 的下一代是 Spanner ,F(xiàn)1 是在 Spanner 上面構(gòu)建的。

    TiDB and TiKV

    TiKV 和 TiDB 基本上對應(yīng) Google Spanner 和 Google F1,用 Open Source 方式重建。目前這兩個(gè)項(xiàng)目都開放在 GitHub 上面,兩個(gè)項(xiàng)目都比較火爆,TiDB 是更早一點(diǎn)開源的, 目前 TiDB 在 GitHub 上 有 4300 多個(gè) Star,每天都在增長。

    另外,對于現(xiàn)在的社會來講,我們覺得 Infrastructure 領(lǐng)域閉源的東西是沒有任何生存機(jī)會的。沒有任何一家公司,愿意把自己的身家性命壓在一個(gè)閉源的項(xiàng)目上。舉一個(gè)很典型的例子,在美國有一個(gè)數(shù)據(jù)庫叫 FoundationDB,去年被蘋果收購了。FoundationDB 之前和用戶簽的合約都是一年的合約。比如說,我給你服務(wù)周期是一年,現(xiàn)在我被另外一個(gè)公司收購了,我今年服務(wù)到期之后,我是滿足合約的。但是其他公司再也不能找它服務(wù)了,因?yàn)樗F(xiàn)在不叫 FoundationDB 了,它叫 Apple了,你不能找 Apple 給你提供一個(gè) Enterprise service。

    TiDB 和 TiKV 為什么是兩個(gè)項(xiàng)目,因?yàn)樗?Google 的內(nèi)部架構(gòu)對比差不多是這樣的:TiKV 對應(yīng)的是 Spanner,TiDB 對應(yīng)的是 F1 。F1 里面更強(qiáng)調(diào)上層的分布式的 SQL 層到底怎么做,分布式的 Plan 應(yīng)該怎么做,分布式的 Plan 應(yīng)該怎么去做優(yōu)化。同時(shí) TiDB 有一點(diǎn)做的比較好的是,它兼容了 MySQL 協(xié)議,當(dāng)你出現(xiàn)了一個(gè)新型的數(shù)據(jù)庫的時(shí)候,用戶使用它是有成本的。大家都知道作為開發(fā)很討厭的一個(gè)事情就是,我要每個(gè)語言都寫一個(gè) Driver,比如說你要支持 C++、你要支持 Java、你要支持 Go 等等,這個(gè)太累了,而且用戶還得改他的程序,所以我們選擇了一個(gè)更加好的東西兼容 MySQL 協(xié)議,讓用戶可以不用改。一會我會用一個(gè)視頻來演示一下,為什么一行代碼不改就可以用,用戶就能體會到 TiDB 帶來的所有的好處。

    這個(gè)圖實(shí)際上是整個(gè)協(xié)議棧或者是整個(gè)軟件棧的實(shí)現(xiàn)。大家可以看到整個(gè)系統(tǒng)是高度分層的,從最底下開始是 RocksDB ,然后再上面用 Raft 構(gòu)建一層可以被復(fù)制的 RocksDB ,在這一層的時(shí)候它還沒有 Transaction,但是整個(gè)系統(tǒng)現(xiàn)在的狀態(tài)是所有寫入的數(shù)據(jù)一定要保證它復(fù)制到了足夠多的副本。也就是說只要我寫進(jìn)來的數(shù)據(jù)一定有足夠多的副本去 cover 它,這樣才比較安全,在一個(gè)比較安全的 Key-value store 上面, 再去構(gòu)建它的多版本,再去構(gòu)建它的分布式事務(wù),然后在分布式事務(wù)構(gòu)建完成之后,就可以輕松的加上 SQL 層,再輕松的加上MySQL 協(xié)議的支持。然后,這兩天我比較好奇,自己寫了 MongoDB 協(xié)議的支持,然后我們可以用 MongoDB 的客戶端來玩,就是說協(xié)議這一層是高度可插拔的。TiDB 上可以在上面構(gòu)建一個(gè) MongoDB 的協(xié)議,相當(dāng)于這個(gè)是構(gòu)建一個(gè) SQL 的協(xié)議,可以構(gòu)建一個(gè) NoSQL 的協(xié)議。這一點(diǎn)主要是用來驗(yàn)證 TiKV 在模型上面的支持能力。

    這是整個(gè) TiKV 的架構(gòu)圖,從這個(gè)看來,整個(gè)集群里面有很多 Node,比如這里畫了四個(gè) Node ,分別對應(yīng)了四個(gè)機(jī)器。每一個(gè) Node 上可以有多個(gè) Store,每個(gè) Store 里面又會有很多小的 Region,就是說一小片數(shù)據(jù),就是一個(gè) Region 。從全局來看所有的數(shù)據(jù)被劃分成很多小片,每個(gè)小片默認(rèn)配置是 64M,它已經(jīng)足夠小,可以很輕松的從一個(gè)節(jié)點(diǎn)移到另外一個(gè)節(jié)點(diǎn),Region 1 有三個(gè)副本,它分別在 Node1、Node 2 和 Node4 上面, 類似的Region 2,Region 3 也是有三個(gè)副本。每個(gè) Region 的所有副本組成一個(gè) Raft Group,整個(gè)系統(tǒng)可以看到很多這樣的 Raft groups。

    Raft 細(xì)節(jié)我不展開了,大家有興趣可以找我私聊或者看一下相應(yīng)的資料。

    因?yàn)檎麄€(gè)系統(tǒng)里面我們可以看到上一張圖里面有很多 Raft group 給我們,不同 Raft group 之間的通訊都是有開銷的。所以我們有一個(gè)類似于 MySQL 的 group commit 機(jī)制 ,你發(fā)消息的時(shí)候?qū)嶋H上可以 share 同一個(gè) connection , 然后 pipeline + batch 發(fā)送,很大程度上可以省掉大量 syscall 的開銷。

    另外,其實(shí)在一定程度上后面我們在支持壓縮的時(shí)候,也有非常大的幫助,就是可以減少數(shù)據(jù)的傳輸。對于整個(gè)系統(tǒng)而言,可能有數(shù)百萬的 Region,它的大小可以調(diào)整,比如說 64M、128M、256M,這個(gè)實(shí)際上依賴于整個(gè)系統(tǒng)里面當(dāng)前的狀況。

    比如說我們曾經(jīng)在有一個(gè)用戶的機(jī)房里面做過測試,這個(gè)測試有一個(gè)香港機(jī)房和新加坡的機(jī)房。結(jié)果我們在做復(fù)制的時(shí)候,新加坡的機(jī)房大于 256M 就復(fù)制不過去,因?yàn)闄C(jī)房很不穩(wěn)定,必須要保證數(shù)據(jù)切的足夠小,這樣才能復(fù)制過去。

    如果一個(gè) Region 太大以后我們會自動(dòng)做 SPLIT,這是非常好玩的過程,有點(diǎn)像細(xì)胞的分裂。

    然后 TiKV 的 Raft 實(shí)現(xiàn),是從 etcd 里面 port 過來的,為什么要從 etcd 里面 port 過來呢?首先 TiKV 的 Raft 實(shí)現(xiàn)是用 Rust 寫的。作為第一個(gè)做到生產(chǎn)級別的 Raft 實(shí)現(xiàn),所以我們從 etcd 里面把它用 Go 語言寫的 port 到這邊。

    這個(gè)是 Raft 官網(wǎng)上面列出來的 TiKV在里面的狀態(tài),大家可以看到 TiKV 把所有 Raft 的 feature 都實(shí)現(xiàn)了。 比如說 Leader Election、Membership Changes,這個(gè)是非常重要的,整個(gè)系統(tǒng)的 scale 過程高度依賴 Membership Changes,后面我用一個(gè)圖來講這個(gè)過程。后面這個(gè)是 Log Compaction,這個(gè)用戶不太關(guān)心。

    這是很典型的細(xì)胞分裂的圖,實(shí)際上 Region 的分裂過程和這個(gè)是類似的。

    我們看一下擴(kuò)容是怎么做的。

    比如說以現(xiàn)在的系統(tǒng)假設(shè),我們剛開始說只有三個(gè)節(jié)點(diǎn),有 Region1 分別是在 1 、2、4,我用虛線連接起來代表它是一個(gè) Raft group ,大家可以看到整個(gè)系統(tǒng)里面有三個(gè) Raft group ,在每一個(gè) Node 上面數(shù)據(jù)的分布是比較均勻的,在這個(gè)假設(shè)每一個(gè) Region 是 64M ,相當(dāng)于只有一個(gè) Node 上面負(fù)載比其他的稍微大一點(diǎn)點(diǎn)。

    一個(gè)在線視頻默認(rèn)我們都是推薦 3 個(gè)副本或者 5 個(gè)副本的配置。Raft 本身有一個(gè)特點(diǎn),如果一個(gè) leader down 掉之后,其它的節(jié)點(diǎn)會選一個(gè)新的 leader ,那么這個(gè)新的 leader 會把它還沒有 commit 但已經(jīng) reply 過去的 log 做一個(gè) commit ,然后會再做 apply ,這個(gè)有點(diǎn)偏 Raft 協(xié)議,細(xì)節(jié)我不講了。

    復(fù)制數(shù)據(jù)的小的 Region,它實(shí)際上是跨多個(gè)數(shù)據(jù)中心做的復(fù)制。這里面最重要的一點(diǎn)是永遠(yuǎn)不丟失數(shù)據(jù),無論如何我保證我的復(fù)制一定是復(fù)制到 majority ,任何時(shí)候我只要對外提供服務(wù),允許外面寫入數(shù)據(jù)一定要復(fù)制到 majority 。很重要的一點(diǎn)就是恢復(fù)的過程一定要是自動(dòng)化的,我前面已經(jīng)強(qiáng)調(diào)過,如果不能自動(dòng)化恢復(fù),那么中間的宕機(jī)時(shí)間或者對外不可服務(wù)的時(shí)間,便不是由整個(gè)系統(tǒng)決定的,這是相對回到了幾十年前的狀態(tài)。

    MVCC

    MVCC 我稍微仔細(xì)講一下這一塊。MVCC 的好處,它很好支持 Lock-free 的 snapshot read ,一會兒我有一個(gè)圖會展示 MVCC 是怎么做的。isolation level 就不講了, MySQL 里面的級別是可以調(diào)的,我們的 TiKV 有 SI,還有 SI+lock,默認(rèn)是支持 SI 的這種隔離級別,然后你寫一個(gè) select for update 語句,這個(gè)會自動(dòng)的調(diào)整到 SI 加上 lock 這個(gè)隔離級別。這個(gè)隔離級別基本上和 SSI 是一致的。還有一個(gè)就是 GC 的問題,如果你的系統(tǒng)里面的數(shù)據(jù)產(chǎn)生了很多版本,你需要把這個(gè)比較老的數(shù)據(jù)給 GC 掉,比如說正常情況下我們是不刪除數(shù)據(jù)的, 你寫入一行,然后再寫入一行,不斷去 update 同一行的時(shí)候,每一次 update 會產(chǎn)生新的版本,新的版本就會在系統(tǒng)里存在,所以我們需要一個(gè) GC 的模塊把比較老的數(shù)據(jù)給 GC 掉,實(shí)際上這個(gè) GC 不是 Go 里面的GC,不是 Java 的 GC,而是數(shù)據(jù)的 GC。

    這是一個(gè)數(shù)據(jù)版本,大家可以看到我們的數(shù)據(jù)分成兩塊,一個(gè)是 meta,一個(gè)是 data。meta 相對于描述我的數(shù)據(jù)當(dāng)前有多少個(gè)版本。大家可以看到綠色的部分,比如說我們的 meta key 是 A ,keyA 有三個(gè)版本,是 A1 、A2、A3,我們把 key 自己和 version 拼到一起。那我們用 A1、A2、A3 分別描述 A 的三個(gè)版本,那么就是 version 1/2/3。meta 里面描述,就是我的整個(gè) key 相對應(yīng)哪個(gè)版本,我想找到那個(gè)版本。比如說我現(xiàn)在要讀取 key A 的版本10,但顯然現(xiàn)在版本 10 是沒有的,那么小于版本 10 最大的版本是 3,所以這時(shí)我就能讀取到 3,這是它的隔離級別決定的。關(guān)于 data,我剛才已經(jīng)講過了。

    分布式事務(wù)模型

    接下來是分布式事務(wù)模型,其實(shí)是基于 Google Percolator,這是 Google 在 2006 發(fā)表的一篇論文,是 Google 在做內(nèi)部增量處理的時(shí)候發(fā)現(xiàn)了這個(gè)方法,本質(zhì)上還是二階段提交的。這使用的是一個(gè)樂觀鎖,比如說我提供一個(gè) transaction ,我去改一個(gè)東西,改的時(shí)候是發(fā)布在本地的,并沒有馬上 commit 到數(shù)據(jù)存儲那一端,這個(gè)模型就是說,我修改的東西我馬上去 Lock 住,這個(gè)基本就是一個(gè)悲觀鎖。但如果到最后一刻我才提交出去,那么鎖住的這一小段的時(shí)間,這個(gè)時(shí)候?qū)崿F(xiàn)的是樂觀鎖。樂觀鎖的好處就是當(dāng)你沖突很小的時(shí)候可以得到非常好的性能,因?yàn)闆_突特別小,所以我本地修改通常都是有效的,所以我不需要去 Lock ,不需要去 roll back 。本質(zhì)上分布式事務(wù)就是 2PC (兩階段提交) 或者是 2+x PC,基本上沒有 1PC,除非你在別人的級別上做弱化。比如說我允許你讀到當(dāng)前最新的版本,也允許你讀到前面的版本,書里面把這個(gè)叫做幻讀。如果你調(diào)到這個(gè)程度是比較容易做 1PC 的,這個(gè)實(shí)際上還是依賴用戶設(shè)定的隔離級別的,如果用戶需要更高的隔離級別,這個(gè) 1PC就不太好做了。

    這是一個(gè)路由,正常來講,大家可能會好奇一個(gè) SQL 語句怎么最后會落到存儲層,然后能很好的運(yùn)行,最后怎么能映射到 KV 上面,又怎么能路由到正確的節(jié)點(diǎn),因?yàn)檎麄€(gè)系統(tǒng)可能有上千個(gè)節(jié)點(diǎn),你怎么能正確路由到那一個(gè)的節(jié)點(diǎn)。我們在 TiDB 有一個(gè) TiKV driver , 另外 TiKV 對外使用的是 Google Protocol Buffer 來作為通訊的編碼格式。

    Placement Driver

    來說一下 Placement Driver 。Placement Driver 是什么呢?整個(gè)系統(tǒng)里面有一個(gè)節(jié)點(diǎn),它會時(shí)刻知道現(xiàn)在整個(gè)系統(tǒng)的狀態(tài)。比如說每個(gè)機(jī)器的負(fù)載,每個(gè)機(jī)器的容量,是否有新加的機(jī)器,新加機(jī)器的容量到底是怎么樣的,是不是可以把一部分?jǐn)?shù)據(jù)挪過去,是不是也是一樣下線, 如果一個(gè)節(jié)點(diǎn)在十分鐘之內(nèi)無法被其他節(jié)點(diǎn)探測到,我認(rèn)為它已經(jīng)掛了,不管它實(shí)際上是不是真的掛了,但是我也認(rèn)為它掛了。因?yàn)檫@個(gè)時(shí)候是有風(fēng)險(xiǎn)的,如果這個(gè)機(jī)器萬一真的掛了,意味著你現(xiàn)在機(jī)器的副本數(shù)只有兩個(gè),有一部分?jǐn)?shù)據(jù)的副本數(shù)只有兩個(gè)。那么現(xiàn)在你必須馬上要在系統(tǒng)里面重新選一臺機(jī)器出來,它上面有足夠的空間,讓我現(xiàn)在只有兩個(gè)副本的數(shù)據(jù)重新再做一份新的復(fù)制,系統(tǒng)始終維持在三個(gè)副本。整個(gè)系統(tǒng)里面如果機(jī)器掛掉了,副本數(shù)少了,這個(gè)時(shí)候應(yīng)該會被自動(dòng)發(fā)現(xiàn),馬上補(bǔ)充新的副本,這樣會維持整個(gè)系統(tǒng)的副本數(shù)。這是很重要的 ,為了避免數(shù)據(jù)丟失,必須維持足夠的副本數(shù),因?yàn)楦北緮?shù)每少一個(gè),你的風(fēng)險(xiǎn)就會再增加。這就是 Placement Driver 做的事情。

    同時(shí),Placement Driver 還會根據(jù)性能負(fù)載,不斷去 move 這個(gè) data 。比如說你這邊負(fù)載已經(jīng)很高了,一個(gè)磁盤假設(shè)有 100G,現(xiàn)在已經(jīng)用了 80G,另外一個(gè)機(jī)器上也是 100G,但是他只用了 20G,所以這上面還可以有幾十 G 的數(shù)據(jù),比如 40G 的數(shù)據(jù),你可以 move 過去,這樣可以保證系統(tǒng)有很好的負(fù)載,不會出現(xiàn)一個(gè)磁盤巨忙無比,數(shù)據(jù)已經(jīng)多的裝不下了,另外一個(gè)上面還沒有東西,這是 Placement Driver 要做的東西。

    Raft 協(xié)議還提供一個(gè)很高級的特性叫 leader transfer。leader transfer 就是說在我不移動(dòng)數(shù)據(jù)的時(shí)候,我把我的 leadership 給你,相當(dāng)于從這個(gè)角度來講,我把流量分給你,因?yàn)槲沂?leader,所以數(shù)據(jù)會到我這來,但我現(xiàn)在把 leader給你,我讓你來當(dāng) leader,原來打給我的請求會被打給你,這樣我的負(fù)載就降下來。這就可以很好的動(dòng)態(tài)調(diào)整整個(gè)系統(tǒng)的負(fù)載,同時(shí)又不搬移數(shù)據(jù)。不搬移數(shù)據(jù)的好處就是,不會形成一個(gè)抖動(dòng)。

    MySQL Sharding

    MySQL Sharding 我前面已經(jīng)提到了它的各種天花板,MySQL Sharding 的方案很典型的就是解決基本問題以后,業(yè)務(wù)稍微復(fù)雜一點(diǎn),你在 sharding 這一層根本搞不定。它永遠(yuǎn)需要一個(gè) sharding key,你必須要告訴我的 proxy,我的數(shù)據(jù)要到哪里找,對用戶來說是極不友好的,比如我現(xiàn)在是一個(gè)單機(jī)的,現(xiàn)在我要切入到一個(gè)分布式的環(huán)境,這時(shí)我必須要改我的代碼,我必須要知道我這個(gè) key ,我的 row 應(yīng)該往哪里 Sharding。如果是用 ORM ,這個(gè)基本上就沒法做這個(gè)事情了。有很多 ORM 它本身假設(shè)我后面只有一個(gè) MySQL。但 TiDB 就可以很好的支持,因?yàn)槲宜械慕巧际菍Φ模也恍枰P(guān)注 Sharding 、分庫、分表這類的事情。

    這里面有一個(gè)很重要的問題沒有提,我怎么做 DDL。如果這個(gè)表非常大的話,比如說我們有一百億吧,橫跨了四臺機(jī)器,這個(gè)時(shí)候你要給它做一個(gè)新的 Index,就是我要添加一個(gè)新的索引,這個(gè)時(shí)候你必須要不影響任何現(xiàn)有的業(yè)務(wù),實(shí)際上這是多階段提交的算法,這個(gè)是 Google 和 F1 一起發(fā)出來那篇論文。

    簡單來講是這樣的,先把狀態(tài)標(biāo)記成 delete only ,delete only 是什么意思呢?因?yàn)樵诜植际较到y(tǒng)里面,所有的系統(tǒng)對于 schema 的視野不是一致的,比如說我現(xiàn)在改了一個(gè)值,有一部分人發(fā)現(xiàn)這個(gè)值被改了,但是還有一部分人還沒有開始訪問這個(gè),所以根本不知道它被改了。然后在一個(gè)分布系統(tǒng)里,你也不可能實(shí)時(shí)通知到所有人在同一時(shí)刻發(fā)現(xiàn)它改變了。比如說從有索引到?jīng)]有索引,你不能一步切過去,因?yàn)橛械娜苏J(rèn)為它有索引,所以他給它建了一個(gè)索引,但是另外一個(gè)機(jī)器他認(rèn)為它沒有索引,所以他就把數(shù)據(jù)給刪了,索引就留在里面了。這樣遇到一個(gè)問題,我通過索引找的時(shí)候告訴我有, 實(shí)際數(shù)據(jù)卻沒有了,這個(gè)時(shí)候一致性出了問題。比如說我 count 一個(gè) email 等于多少的,我通過 email 建了一個(gè)索引,我認(rèn)為它是在,但是 UID 再轉(zhuǎn)過去的時(shí)候可能已經(jīng)不存在了。

    比如說我先標(biāo)記成 delete only,我刪除它的時(shí)候不管它現(xiàn)在有沒有索引,我都會嘗試刪除索引,所以我的數(shù)據(jù)是干凈的。如果我刪除掉的話,我不管結(jié)果是什么樣的,我嘗試去刪一下,可能這個(gè)索引還沒 build 出來,但是我仍然刪除,如果數(shù)據(jù)沒有了,索引一定沒有了,所以這可以很好的保持它的一致性。后面再類似于前面,先標(biāo)記成 write only 這種方式,連續(xù)再迭代這個(gè)狀態(tài),就可以迭代到一個(gè)最終可以對外公開的狀態(tài)。比如說當(dāng)我迭代到一定程度的時(shí)候,我可以從后臺 build index ,比如說我一百億,正在操作的 index 會馬上 build,但是還有很多沒有 build index ,這個(gè)時(shí)候后臺不斷的跑 map-reduce 去 build index ,直到整個(gè)都 build 完成之后,再對外 public ,就是說我這個(gè)索引已經(jīng)可用了,你可以直接拿索引來找,這個(gè)是非常經(jīng)典的。在這個(gè) Online,Asynchronous Schema Change in F1 paper之前,大家都不知道這事該怎么做。

    Proxy Sharding 的方案不支持分布式事務(wù),更不用說跨數(shù)據(jù)中心的一致性事務(wù)了。 TiKV 很好的支持 transaction,剛才提到的 Raft 除了增加副本之外,還有 leader transfer,這是一個(gè)傳統(tǒng)的方案都無法提供的特性。以及它帶來的好處,當(dāng)我瞬間平衡整個(gè)系統(tǒng)負(fù)載的時(shí)候,對外是透明的, 做 leader transfer 的時(shí)候并不需要移動(dòng)數(shù)據(jù),只是個(gè)簡單的 leader transfer 消息。

    然后說一下如果大家想?yún)⑴c我們項(xiàng)目的話是怎樣的過程,因?yàn)檎麄€(gè)系統(tǒng)是完全開源的,如果大家想?yún)⑴c其中任何一部分都可以,比如說我想?yún)⑴c到分布式 KV,可以直接貢獻(xiàn)到 TiKV。TiKV 需要寫 Rust,如果大家對這塊特別有激情可以體驗(yàn)寫 Rust 的感覺 。

    TiDB 是用 Go 寫的,Go 在中國的群眾基礎(chǔ)是非常多的,目前也有很多人在貢獻(xiàn)。整個(gè) TiDB 和TiKV 是高度協(xié)作的項(xiàng)目,因?yàn)?TiDB 目前還用到了 etcd ,我們在和 CoreOS 在密切的合作,也特別感謝 CoreOS 幫我們做了很多的支持,我們也為 CoreOS 的 etcd 提了一些 patch。同時(shí),TiKV 使用 RocksDB ,所以我們也為 RocksDB 提了一些 patch 和 test,我們也非常感謝 Facebook RocksDB team 對我們項(xiàng)目的支持。

    另外一個(gè)是 PD,就是我們前面提的 Placement Driver,它負(fù)責(zé)監(jiān)控整個(gè)系統(tǒng)。這部分的算法比較好玩,大家如果有興趣的話,可以去自己控制整個(gè)集群的調(diào)度,它和 Kubernetes 或者是Mesos 的調(diào)度算法是不一樣的,因?yàn)樗{(diào)度的維度實(shí)際上比那個(gè)要更多。比如說磁盤的容量,你的 leader 的數(shù)量,你的網(wǎng)絡(luò)當(dāng)前的使用情況,你的 IO 的負(fù)載和 CPU 的負(fù)載都可以放進(jìn)去。同時(shí)你還可以讓它調(diào)度不要跨一個(gè)機(jī)房里面建多個(gè)副本。

    posted @ 2016-08-23 15:12 小馬歌 閱讀(292) | 評論 (0)編輯 收藏
     
    from:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0831/3395.html

    編輯推薦:稀土掘金,這是一個(gè)針對技術(shù)開發(fā)者的一個(gè)應(yīng)用,你可以在掘金上獲取最新最優(yōu)質(zhì)的技術(shù)干貨,不僅僅是Android知識、前端、后端以至于產(chǎn)品和設(shè)計(jì)都有涉獵,想成為全棧工程師的朋友不要錯(cuò)過!

    原文:http://www.csdn.net/article/2015-08-31/2825579 

    JSON——大家可能都知道它是幾乎所有現(xiàn)代服務(wù)器都使用的輕量級數(shù)據(jù)交換格式。它體量輕,可讀性強(qiáng),通常比老式的、不友好的XML開發(fā)起來更加便捷友好。JSON是不依賴于開發(fā)語言的數(shù)據(jù)格式,但是在解析數(shù)據(jù)并將其轉(zhuǎn)換到如Java對象時(shí),會消耗我們的時(shí)間和存儲資源。

    幾天前,F(xiàn)acebook宣布,其Android應(yīng)用程序大幅提升了數(shù)據(jù)處理性能。這是由于幾乎在全部應(yīng)用程序中放棄了JSON數(shù)據(jù)格式,用FlatBuffers取而代之了。閱讀這篇文章可以獲得關(guān)于FlatBuffers的基礎(chǔ)知識,學(xué)會如何從JSON轉(zhuǎn)換到FlatBuffers。

    雖然這東西是非常有前景的,但是乍一看其實(shí)現(xiàn)過程不是一下子就能明白的。而且Facebook也沒有說得很詳細(xì)。這就是為什么我要寫這篇文章,在其中展示我們是如何使用Flatbuffers開展工作的。

    FlatBuffers

    總之,FlatBuffers是Google專門為游戲開發(fā)而創(chuàng)建的跨平臺序列化庫,就像Facebook所展示的那樣,它在Android平臺上遵循快速響應(yīng)UI的16ms規(guī)則

    但是,在把所有數(shù)據(jù)遷移到FlatBuffers之前,你要確定確實(shí)需要這樣做。因?yàn)椋@樣做有時(shí)對性能的影響是潛移默化的,而且數(shù)據(jù)安全性要比計(jì)算速度上幾十毫秒的差異更重要。

    什么使得Flatbuffers如此奏效?


    • 由于是以二進(jìn)制形式緩存,訪問序列化數(shù)據(jù)時(shí)也無需數(shù)據(jù)解析過程。即使對于層次化數(shù)據(jù)也不需要解析。多虧不需要初始化解析器(初始化意味著要建立復(fù)雜的字段映射)和解析數(shù)據(jù),這些都是需要花費(fèi)時(shí)間的。

    • Flatbuffers數(shù)據(jù)不需要分配比自身使用緩沖區(qū)還要多的內(nèi)存。我們不必像在JSON中那樣為解析數(shù)據(jù)的整個(gè)層次分配額外對象。

    要獲得正宗的數(shù)據(jù),就再讀一讀Facebook上關(guān)于FlatBuffers遷移問題的文章,還有Google自己的文檔。


    實(shí)現(xiàn)

    本文將介紹在Android應(yīng)用程序中使用Flatbuffers的最簡單方法:


    • JSON數(shù)據(jù)在應(yīng)用程序之外的某個(gè)地方被轉(zhuǎn)換成FlatBuffer格式的文件(例如,將二進(jìn)制數(shù)據(jù)以文件的形式提交,還可以從API直接返回FlatBuffer二進(jìn)制文件)。

    • 在flatc (FlatBuffer編譯器)的幫助下,手工生成數(shù)據(jù)模型(Java類)。

    • JSON文件存在一定的局限性(不能使用null字段,日期格式也被解析為字符串)。


    將來,我們或許會提出更復(fù)雜的解決方案。

    FlatBuffers編譯器

    首先,我們需要flatc,即flatbuffers編譯器。該編譯器可以從Google所屬的源代碼構(gòu)建,源代碼位于Flatbuffers資源庫中。我們下載并克隆它。整個(gè)構(gòu)建過程在FlatBuffers構(gòu)建文檔中都做了描述。如果你是Mac用戶的話,需要這樣來構(gòu)建:


    1. 在\{extract directory}\build\XcodeFlatBuffers.xcodeproj路徑下,打開已下載的源代碼。

    2. 點(diǎn)擊Play按鈕或⌘ + R,運(yùn)行flatc scheme(默認(rèn)情況下應(yīng)該是被選中的)。

    3. flatc可執(zhí)行文件就會在項(xiàng)目的根目錄下出現(xiàn)。


    現(xiàn)在,我們可以使用schema編譯器了,該編譯器能夠把給定的schema(在Java、C#、Python、GO和C++語言中的schema)生成為模型類,還可以把JSON轉(zhuǎn)換成Flatbuffer的二進(jìn)制文件。

    Schema文件

    接著,我們必須準(zhǔn)備schema文件,該文件定義了要進(jìn)行序列化和反序列化的數(shù)據(jù)結(jié)構(gòu)。這個(gè)schema將用于flatc創(chuàng)建Java模型,把JSON轉(zhuǎn)換成FlatBuffers的二進(jìn)制文件。

    這里是JSON文件的一部分。

    完整版本在這里。這是略微修改后的版本,可以從Github API調(diào)用:https://api.github.com/users/google/repos 。

    Flatbuffer schema是編寫得很好的文檔,所以就不深入探討這個(gè)問題了。另外,本文中的schema不會很復(fù)雜。我們所要做的僅僅是創(chuàng)建3張表:ReposList, Repo和User,并定義root_type。這是schema的重要組成部分。

    完整的schema文件在這里

    FlatBuffers數(shù)據(jù)文件

    真棒,我們現(xiàn)在要做的是把repos_json.json轉(zhuǎn)換成FlatBuffers二進(jìn)制文件,生成能夠以Java風(fēng)格表示數(shù)據(jù)的Java模型(此處操作所需的全部文件都在我們的代碼庫中):

    $ ./flatc -j -b repos_schema.fbs repos_json.json

    如果一切順利,會產(chǎn)生下列文件:


    • repos_json.bin(要將重命名它為 repos_flat.bin)

    • Repos/Repo.java

    • Repos/ReposList.java

    • Repos/User.java


    Android應(yīng)用程序

    現(xiàn)在來創(chuàng)建示例程序,在實(shí)踐中來看看Flatbuffers格式是如何起作用的。這是截圖:

    blob.png

    在UI部分,ProgressBar僅用于顯示不恰當(dāng)?shù)臄?shù)據(jù)處理對用戶界面順暢度的影響。

    應(yīng)用文件看起來是這個(gè)樣子:app/build.gradle

    當(dāng)然,在本例中不是必須要用Rx或ButterKnife這樣的視圖注入利器,但是為什么不讓應(yīng)用更細(xì)致一些呢??

    我們把repos_flat.bin 和 repos_json.json文件放到res/raw/目錄下。RawDataReader是工具類,它幫助我們讀取Android應(yīng)用中的原始文件。

    最后,把Repo,ReposList和User這三張表對應(yīng)的模型類代碼放到項(xiàng)目源代碼中。

    FlatBuffers庫

    使用Java語言編程過程中,F(xiàn)latBuffers提供了可以直接處理這種數(shù)據(jù)格式的庫,也這是flatbuffers-java-1.2.0-SNAPSHOT.jar文件。如果你想手工生成該文件,需要下載FlatBuffers源代碼,再到目錄java/下,用Maven生成該庫:

    $ mvn install

    現(xiàn)在將.jar文件放到Android項(xiàng)目的app/libs/目錄下。

    好了,當(dāng)務(wù)之急是實(shí)現(xiàn)MainActivity類,這是完整源代碼。

    我們最為關(guān)注的兩個(gè)方法是:


    • parseReposListJson(String reposStr) - 這個(gè)方法初始化Gson解析器,并把JSON字符串轉(zhuǎn)換成Java對象。

    • loadFlatBuffer(byte[] bytes)  - 這個(gè)方法將字節(jié)(repos_flat.bin文件)轉(zhuǎn)換成Java對象。


    使用FlatBuffers的結(jié)果

    現(xiàn)在讓我們把JSON和FlatBuffers在加載時(shí)間和資源消耗方面的差異形象化。測試是在帶有Android M(beta版)的Nexus 5上進(jìn)行的。

    加載時(shí)間

    測量的過程是將其他文件轉(zhuǎn)換為Java源文件,對所有(90個(gè))元素進(jìn)行迭代。


    • 使用JSON:JSON文件(大小:478kB)平均加載時(shí)間200ms(時(shí)間區(qū)間:180ms~250ms);

    • 使用FlatBuffers:FlatBuffers二進(jìn)制文件(大小:352kB)平均加載時(shí)間5ms(時(shí)間區(qū)間:3ms~10ms)。


    記得16ms規(guī)則嗎?我們在UI線程中調(diào)用這些方法的原因就是要看看在這種情況下界面表現(xiàn)如何:

    JSON數(shù)據(jù)加載效果:

    json.gif

    FlatBuffers數(shù)據(jù)加載效果:

    flatbuffers.gif

    看出區(qū)別了嗎?JSON數(shù)據(jù)的加載過程中, ProgressBar停頓了一會,界面不是那么順暢(加載時(shí)間超過了16ms)。

    內(nèi)存分配、CPU等資源

    還有什么想要測量的嗎?也許應(yīng)該測量一下Android Studio 1.3,還有那些新特性。例如,內(nèi)存分配跟蹤器(Allocation Tracker),內(nèi)存狀態(tài)查看器(Memory Viewer)和方法跟蹤器(Method Tracer)。

    源代碼

    這里所講解項(xiàng)目的完整源代碼都在Github代碼庫中。你不需要接觸整個(gè)FlatBuffers項(xiàng)目,所需的內(nèi)容全都在flatbuffers/目錄下。

    ( 翻譯/張揮戈  友情審校/白云鵬)

    文章來源:froger_mcs dev blog

    作者簡介:

    Miroslaw Stanek,Azimo Money Transfer公司移動(dòng)項(xiàng)目負(fù)責(zé)人,Android和iOS平臺程序員,視頻游戲玩家,冰雪運(yùn)動(dòng)愛好者。個(gè)人博客:http://frogermcs.github.io

    posted @ 2016-08-18 16:26 小馬歌 閱讀(608) | 評論 (0)編輯 收藏
     
    from:http://www.oschina.net/news/75092/android-flatbuffers-json

    你可能會問,既然我們已經(jīng)有很標(biāo)準(zhǔn)的JSON以及轉(zhuǎn)換庫比如GSON和Jackson,為什么還要使用新的工具呢?

    不妨先試一下FlatBuffers,然后你就會發(fā)現(xiàn)它比JSON快得多。

    FlatBuffers是什么?

    FlatBuffers是一個(gè)高效的跨平臺序列化類庫,可以在C++、C#、C、Go、Java、JavaScript、PHP和Python中使用。是Google開發(fā)的,是為了應(yīng)用在游戲開發(fā),以及其他注重性能的應(yīng)用上。

    為什么要使用FlatBuffers?

    • 不需要解析/拆包就可以訪問序列化數(shù)據(jù) — FlatBuffers與其他庫不同之處就在于它使用二進(jìn)制緩沖文件來表示層次數(shù)據(jù),這樣它們就可以被直接訪問而不需解析與拆包,同時(shí)還支持?jǐn)?shù)據(jù)結(jié)構(gòu)進(jìn)化(前進(jìn)、后退兼容性)。

    • 內(nèi)存高效速度快 — 訪問數(shù)據(jù)時(shí)只需要訪問內(nèi)存中的緩沖區(qū)。它不需要多余的內(nèi)存分配(至少在C++是這樣,其他語言中可能會有變動(dòng))。FlatBuffers還適合配合 mmap或數(shù)據(jù)流使用,只需要緩沖區(qū)的一部分存儲在內(nèi)存中。訪問時(shí)速度接近原結(jié)構(gòu)訪問,只有一點(diǎn)延遲(一種虛函數(shù)表vtable),是為了允許格式升級以 及可選字段。FlatBuffers適合那些花費(fèi)了大量時(shí)間和空間(內(nèi)存分配)來訪問和構(gòu)建序列化數(shù)據(jù)的項(xiàng)目,比如游戲以及其他對表現(xiàn)敏感的應(yīng)用。可以參 考這里的基準(zhǔn)

    • 靈活 — 由于有可選字段,你不但有很強(qiáng)的升級和回退兼容性(對于歷史悠久的游戲尤其重要,不用為了每個(gè)版本升級所有數(shù)據(jù)),在選擇要存儲哪些數(shù)據(jù)以及設(shè)計(jì)數(shù)據(jù)結(jié)構(gòu)時(shí)也很自由。

    • 輕量的code footprint — FlatBuffers只需要很少量的生成代碼,以及一個(gè)表示最小依賴的很小的頭文件,很容易集成。細(xì)節(jié)上可以看上面的基準(zhǔn)頁。

    • 強(qiáng)類型 — 編譯時(shí)報(bào)錯(cuò),而不需要自己寫重復(fù)的容易出錯(cuò)的運(yùn)行時(shí)檢查。它可以自動(dòng)生成有用的代碼。

    • 使用方便 — 生成的C++代碼允許精簡訪問與構(gòu)建代碼。還有可選的用于實(shí)現(xiàn)圖表解析、類似JSON的運(yùn)行時(shí)字符串展示等功能的方法。(后者比JSON解析庫更快,內(nèi)存效率更高)

    • 代碼跨平臺且沒有依賴 — C++代碼可以運(yùn)行在任何近代的gcc/clang和VS2010上。同時(shí)還有用于測試和范例的構(gòu)建文件(Android中.mk文件,其他平臺是cmake文件)。

    都有誰使用FlatBuffers?

    • BobbleApp,印度第一貼圖App。我們在BobbleApp中使用FlatBuffers后App的性能明顯增強(qiáng)。

    • Cocos2d-x,第一開源移動(dòng)游戲引擎,使用FlatBuffers來序列化所有的游戲數(shù)據(jù)。

    • Facebook使用FlatBuffers在Android App中進(jìn)行客戶端服務(wù)端的溝通。他們寫了一篇文章來描述FlatBuffers是如何加速加載內(nèi)容的。

    • Google的Fun Propulsion Labs在他們所有的庫和游戲中大量使用FlatBuffers。

    App性能有多大提高?

    • 解析速度 解析一個(gè)20KB的JSON流(這差不多是BobbleApp的返回大小)需要35ms,超過了UI刷新間隔也就是16.6ms。如果解析JSON的話,我們就在滑動(dòng)時(shí)就會因?yàn)橐獜拇疟P加載緩存而導(dǎo)致掉幀(視覺上的卡頓)。

    • 解析器初始化 一個(gè)JSON解析器需要先構(gòu)建字段映射再進(jìn)行解析,這會花100ms到200ms,很明顯的拖緩App啟動(dòng)時(shí)間。

    • 垃圾回收 在解析JSON時(shí)創(chuàng)建了很多小對象,在我們的試驗(yàn)中,解析20kb的JSON流時(shí),要分配大約100kb的瞬時(shí)存儲,對Java內(nèi)存回收造成很大壓力。

    FlatBuffers vs JSON

    我嘗試使用FlatBuffers和JSON解析4mb的JSON文件。

    FlatBuffers花了1-5ms,JSON花了大約2000ms。在使用FlatBuffers期間Android App中沒有GC,而在使用JSON時(shí)發(fā)生了很多次GC。在使用JSON時(shí)UI完全卡住,所以真實(shí)使用時(shí)只能在后臺線程進(jìn)行解析。

    如何使用FlatBuffer呢?

    我在我的GitHub中寫了一個(gè)示例,里面手把手教你如何使用FlatBuffer。

    原文鏈接:https://medium.freecodecamp.com/why-consider-flatbuffer-over-json-2e4aa8d4ed07

    稿源:infoQ  作者Amit Shekhar  譯者程大治

    posted @ 2016-08-18 16:17 小馬歌 閱讀(239) | 評論 (0)編輯 收藏
     

    from:http://www.jdon.com/46111


    本文來自Pivotal,分析了中國鐵路總公司12306這個(gè)世界上最大的鐵路系統(tǒng)的火車票預(yù)訂系統(tǒng)。

    在這個(gè)星球上人類最大的年度運(yùn)動(dòng)大概算是中國農(nóng)歷新年,又稱春節(jié)。有3488萬人次通過航空和235萬人次通過鐵路踏上他們的旅途。從歷史上看,鐵路旅行意味著排長龍隊(duì)伍買票,中國鐵路總公司(CRC)現(xiàn)在開始在網(wǎng)上賣火車票,提供比車站售票處或通過電話購買更方便的方法。

    隨著越來越多人的使用車票預(yù)訂系統(tǒng),12306鐵路訂票系統(tǒng)打破了其傳統(tǒng)的RDBMS關(guān)系數(shù)據(jù)庫系統(tǒng),需要重新開始了一個(gè)新的項(xiàng)目,以改善原有系統(tǒng)性能和可伸縮性的問題,能夠承受像春節(jié)度假旅游期間的尖峰的壓力。目前該網(wǎng)站成為中國最受歡迎網(wǎng)站的之一。在這樣嚴(yán)苛的訪問條件下,系統(tǒng)出現(xiàn)以下很差用戶體驗(yàn):使用中斷,性能差,預(yù)訂錯(cuò)誤,支付失敗,票務(wù)確認(rèn)出現(xiàn)問題等等。

    中國鐵道科學(xué)研究院的副主任朱劍圣首先解決性能問題,早在2011年,朱博士確保新系統(tǒng)解決基于下面兩個(gè)性能瓶頸:

    1.關(guān)系型數(shù)據(jù)庫超負(fù)載,以至于不能處理傳入的請求,無論是規(guī)模擴(kuò)展性還是可靠性,都不能滿足SLA要求的水平。 

    2.UNIX服務(wù)器的計(jì)算能力不足以解決容量需求。

    朱博士說:“傳統(tǒng)關(guān)系型數(shù)據(jù)庫和大型機(jī)的計(jì)算模式并不具有擴(kuò)展性,系統(tǒng)不能基于內(nèi)存擴(kuò)展跨多個(gè)節(jié)點(diǎn)上運(yùn)行。我們的網(wǎng)站證明了這一點(diǎn),而試圖擴(kuò)展我們的遺留系統(tǒng)將變得非常昂貴”。

    使用In-memory內(nèi)存數(shù)據(jù)網(wǎng)格解決擴(kuò)展性和可靠性
    朱博士的團(tuán)隊(duì)開始尋找新的解決方案,大型機(jī)被發(fā)現(xiàn)和關(guān)系數(shù)據(jù)庫有同樣的瓶頸,在內(nèi)存數(shù)據(jù)網(wǎng)格(IMDG)領(lǐng)域,他們發(fā)現(xiàn)了Pivotal GemFire,在海運(yùn)貨物系統(tǒng) 金融服務(wù),航空,電子商務(wù)等多個(gè)行業(yè)都擁有成功解決最具挑戰(zhàn)性的數(shù)據(jù)問題的良好記錄。為了執(zhí)行評估,朱博士和他的團(tuán)隊(duì)選擇了國際綜合系統(tǒng)公司(IISI)。IISI擁有強(qiáng)大為政府機(jī)構(gòu)工作的跟蹤記錄,包括在開發(fā)交通運(yùn)輸解決方案,遷移遺留系統(tǒng)到云計(jì)算架構(gòu)等方面經(jīng)歷,有與Pivotal GemFire合作經(jīng)驗(yàn)。他們開始試點(diǎn),相信了GemFire​​將滿足性能,可擴(kuò)展性和可用性的要求,包括能夠在低成本硬件上運(yùn)行。

    IISI創(chuàng)造了一個(gè)概念證明和展示了GemFire幾個(gè)優(yōu)點(diǎn)​​。售票計(jì)算速度提高50到100倍。當(dāng)負(fù)載增加時(shí),響應(yīng)時(shí)間保持10-100毫秒的延遲。他們可以看到,通過增加容量,能實(shí)現(xiàn)近乎線性增長的可擴(kuò)展性和高可用性的能力。項(xiàng)目組在短短兩個(gè)月內(nèi)建立了一個(gè)試點(diǎn),四個(gè)月后,新的在線系統(tǒng)全面部署,跨越5700火車站。

    該小組負(fù)責(zé)鐵路網(wǎng)上預(yù)訂系統(tǒng)每年增長高達(dá)50%。他們的網(wǎng)站每天的平均水平預(yù)訂250萬票。

    72臺UNIX系統(tǒng)和關(guān)系數(shù)據(jù)庫換成了10臺初始和10個(gè)備份的x86服務(wù)器,這是一個(gè)更具成本效益的模式,能在內(nèi)存中處理2TB或一個(gè)月的的火車票數(shù)據(jù)。

    朱博士認(rèn)為:“首先,Pivotal GemFire​​提供了一個(gè)在真實(shí)的測試環(huán)境的證明。然后,在生產(chǎn)環(huán)境面對意想不到的尖峰也是成功的,具體采取了一個(gè)迭代的方法來部署,克服了一系列大規(guī)模的挑戰(zhàn)。在最近的2013春運(yùn)期間,該系統(tǒng)具備了運(yùn)行穩(wěn)定的性能和正常運(yùn)行時(shí)間。現(xiàn)在,我們有一個(gè)可靠,經(jīng)濟(jì)合理的生產(chǎn)體系​​,支持記錄容量增長的空間。這個(gè)規(guī)模實(shí)現(xiàn)10-100毫秒的延遲。”

    基于高可用性,冗余和故障切換機(jī)制上的GemFire​​提供了連續(xù)正常運(yùn)行,它已超出了所有在該領(lǐng)域的CRC校驗(yàn)的指標(biāo),并幫助他們維護(hù)他們的SLA。

    具體PDF下載

    posted @ 2016-08-17 16:40 小馬歌 閱讀(325) | 評論 (0)編輯 收藏
     
         摘要: from:http://www.infoq.com/cn/news/2016/08/Elasticsearch-5-0-Elastic大家好,非常高興能在這里給大家分享,首先簡單自我介紹一下,我叫曾勇,是Elastic的工程師。Elastic將在今年秋季的時(shí)候發(fā)布一個(gè)Elasticsearch V5.0的大版本,這次的微信分享將給大家介紹一下5.0版里面的一些新的特性和改進(jìn)。5.0? 天啦嚕,你是...  閱讀全文
    posted @ 2016-08-13 14:56 小馬歌 閱讀(787) | 評論 (0)編輯 收藏
     
    from:http://www.infoq.com/cn/articles/anatomy-of-an-elasticsearch-cluster-part02

    剖析Elasticsearch集群系列涵蓋了當(dāng)今最流行的分布式搜索引擎Elasticsearch的底層架構(gòu)和原型實(shí)例。

    本文是這個(gè)系列的第二篇,我們將討論Elasticsearch如何處理分布式的三個(gè)C((共識(consensus)、并發(fā)(concurrency)和一致(consistency))的問題、Elasticsearch分片的內(nèi)部概念,比如translog(預(yù)寫日志,WAL(Write Ahead Log)),以及Lucene中的段。

    本系列已經(jīng)得到原文著者Ronak Nathani的授權(quán)

    在本系列的前一篇中,我們討論了Elasticsearch的底層存儲模型及CRUD(創(chuàng)建、讀取、更新和刪除)操作的工作原理。在本文中,我將分享Elasticsearch是如何應(yīng)對分布式系統(tǒng)中的一些基本挑戰(zhàn)的,以及分片的內(nèi)部概念。這其中包括了一些操作方面的事情,Insight Data的工程師們已經(jīng)在使用Elasticsearch構(gòu)建的數(shù)據(jù)平臺之上成功地實(shí)踐并真正理解。我將在本文中主要講述:

    共識——裂腦問題及法定票數(shù)的重要性

    共識是分布式系統(tǒng)的一項(xiàng)基本挑戰(zhàn)。它要求系統(tǒng)中的所有進(jìn)程/節(jié)點(diǎn)必須對給定數(shù)據(jù)的值/狀態(tài)達(dá)成共識。已經(jīng)有很多共識算法諸如RaftPaxos等,從數(shù)學(xué)上的證明了是行得通的。但是,Elasticsearch卻實(shí)現(xiàn)了自己的共識系統(tǒng)(zen discovery),Elasticsearch之父Shay Banon在這篇文章中解釋了其中的原因。zen discovery模塊包含兩個(gè)部分:

    • Ping: 執(zhí)行節(jié)點(diǎn)使用ping來發(fā)現(xiàn)彼此
    • 單播(Unicast):該模塊包含一個(gè)主機(jī)名列表,用以控制哪些節(jié)點(diǎn)需要ping通

    Elasticsearch是端對端的系統(tǒng),其中的所有節(jié)點(diǎn)彼此相連,有一個(gè)master節(jié)點(diǎn)保持活躍,它會更新和控制集群內(nèi)的狀態(tài)和操作。建立一個(gè)新的Elasticsearch集群要經(jīng)過一次選舉,選舉是ping過程的一部分,在所有符合條件的節(jié)點(diǎn)中選取一個(gè)master,其他節(jié)點(diǎn)將加入這個(gè)master節(jié)點(diǎn)。ping間隔參數(shù)ping_interval的默認(rèn)值是1秒,ping超時(shí)參數(shù)ping_timeout的默認(rèn)值是3秒。因?yàn)楣?jié)點(diǎn)要加入,它們會發(fā)送一個(gè)請求給master節(jié)點(diǎn),加入超時(shí)參數(shù)join_timeout的默認(rèn)值是ping_timeout值的20倍。如果master出現(xiàn)問題,那么群集中的其他節(jié)點(diǎn)開始重新ping以啟動(dòng)另一次選舉。這個(gè)ping的過程還可以幫助一個(gè)節(jié)點(diǎn)在忽然失去master時(shí),通過其他節(jié)點(diǎn)發(fā)現(xiàn)master。

    注意:默認(rèn)情況下,client節(jié)點(diǎn)和data節(jié)點(diǎn)不參與這個(gè)選舉過程。可以在elasticsearch.yml配置文件中,通過設(shè)置discovery.zen.master_election.filter_client屬性和discovery.zen.master_election.filter_data屬性為false來改變這種默認(rèn)行為。

    故障檢測的原理是這樣的,master節(jié)點(diǎn)會ping所有其他節(jié)點(diǎn),以檢查它們是否還活著;然后所有節(jié)點(diǎn)ping回去,告訴master他們還活著。

    如果使用默認(rèn)的設(shè)置,Elasticsearch有可能遭到裂腦問題的困擾。在網(wǎng)絡(luò)分區(qū)的情況下,一個(gè)節(jié)點(diǎn)可以認(rèn)為master死了,然后選自己作為master,這就導(dǎo)致了一個(gè)集群內(nèi)出現(xiàn)多個(gè)master。這可能會導(dǎo)致數(shù)據(jù)丟失,也可能無法正確合并數(shù)據(jù)。可以按照如下公式,根據(jù)有資格參加選舉的節(jié)點(diǎn)數(shù),設(shè)置法定票數(shù)屬性的值,來避免爆裂的發(fā)生。

    discovery.zen.minimum_master_nodes = int(# of master eligible nodes/2)+1

    這個(gè)屬性要求法定票數(shù)的節(jié)點(diǎn)加入新當(dāng)選的master節(jié)點(diǎn),來完成并獲得新master節(jié)點(diǎn)接受的master身份。對于確保群集穩(wěn)定性和在群集大小變化時(shí)動(dòng)態(tài)地更新,這個(gè)屬性是非常重要的。圖a和b演示了在網(wǎng)絡(luò)分區(qū)的情況下,設(shè)置或不設(shè)置minimum_master_nodes屬性時(shí),分別發(fā)生的現(xiàn)象。

    注意:對于一個(gè)生產(chǎn)集群來說,建議使用3個(gè)節(jié)點(diǎn)專門做master,這3個(gè)節(jié)點(diǎn)將不服務(wù)于任何客戶端請求,而且在任何給定時(shí)間內(nèi)總是只有1個(gè)活躍。

    我們已經(jīng)搞清楚了Elasticsearch中共識的處理,現(xiàn)在讓我們來看看它是如何處理并發(fā)的。

    并發(fā)

    Elasticsearch是一個(gè)分布式系統(tǒng),支持并發(fā)請求。當(dāng)創(chuàng)建/更新/刪除請求到達(dá)主分片時(shí),它也會被平行地發(fā)送到分片副本上。但是,這些請求到達(dá)的順序可能是亂序的。在這種情況下,Elasticsearch使用樂觀并發(fā)控制,來確保文檔的較新版本不會被舊版本覆蓋。

    每個(gè)被索引的文檔都擁有一個(gè)版本號,版本號在每次文檔變更時(shí)遞增并應(yīng)用到文檔中。這些版本號用來確保有序接受變更。為了確保在我們的應(yīng)用中更新不會導(dǎo)致數(shù)據(jù)丟失,Elasticsearch的API允許我們指定文件的當(dāng)前版本號,以使變更被接受。如果在請求中指定的版本號比分片上存在的版本號舊,請求失敗,這意味著文檔已經(jīng)被另一個(gè)進(jìn)程更新了。如何處理失敗的請求,可以在應(yīng)用層面來控制。Elasticsearch還提供了其他的鎖選項(xiàng),可以通過這篇來閱讀。

    當(dāng)我們發(fā)送并發(fā)請求到Elasticsearch后,接下來面對的問題是——如何保證這些請求的讀寫一致?現(xiàn)在,還無法清楚回答,Elasticsearch應(yīng)落在CAP三角形的哪條邊上,我不打算在這篇文章里解決這個(gè)素來已久的爭辯。

    但是,我們要一起看下如何使用Elasticsearch實(shí)現(xiàn)寫讀一致。

    一致——確保讀寫一致

    對于寫操作而言,Elasticsearch支持的一致性級別,與大多數(shù)其他的數(shù)據(jù)庫不同,允許預(yù)檢查,來查看有多少允許寫入的可用分片。可選的值有quorumoneall。默認(rèn)的設(shè)置為quorum,也就是說只有當(dāng)大多數(shù)分片可用時(shí)才允許寫操作。即使大多數(shù)分片可用,還是會因?yàn)槟撤N原因發(fā)生寫入副本失敗,在這種情況下,副本被認(rèn)為故障,分片將在一個(gè)不同的節(jié)點(diǎn)上重建。

    對于讀操作而言,新的文檔只有在刷新時(shí)間間隔之后,才能被搜索到。為了確保搜索請求的返回結(jié)果包含文檔的最新版本,可設(shè)置replication為sync(默認(rèn)),這將使操作在主分片和副本碎片都完成后才返回寫請求。在這種情況下,搜索請求從任何分片得到的返回結(jié)果都包含的是文檔的最新版本。即使我們的應(yīng)用為了更高的索引率而設(shè)置了replication=async,我們依然可以為搜索請求設(shè)置參數(shù)_preferenceprimary。這樣,搜索請求將查詢主分片,并確保結(jié)果中的文檔是最新版本。

    我們已經(jīng)了解了Elasticsearch如何處理共識、并發(fā)和一致,讓我們來看看分片內(nèi)部的一些主要概念,正是這些特點(diǎn)讓Elasticsearch成為一個(gè)分布式搜索引擎。

    Translog(預(yù)寫日志)

    因?yàn)殛P(guān)系數(shù)據(jù)庫的發(fā)展,預(yù)寫日志(WAL)或者事務(wù)日志(translog)的概念早已遍及數(shù)據(jù)庫領(lǐng)域。在發(fā)生故障的時(shí)候,translog能確保數(shù)據(jù)的完整性。translog的基本原理是,變更必須在數(shù)據(jù)實(shí)際的改變提交到磁盤上之前,被記錄下來并提交。

    當(dāng)新的文檔被索引或者舊的文檔被更新時(shí),Lucene索引將發(fā)生變更,這些變更將被提交到磁盤以持久化。這是一個(gè)很昂貴的操作,如果在每個(gè)請求之后都被執(zhí)行。因此,這個(gè)操作在多個(gè)變更持久化到磁盤時(shí)被執(zhí)行一次。正如我們在上一篇文章中描述的那樣,Lucene提交的沖洗(flush)操作默認(rèn)每30分鐘執(zhí)行一次或者當(dāng)translog變得太大(默認(rèn)512MB)時(shí)執(zhí)行。在這樣的情況下,有可能失去2個(gè)Lucene提交之間的所有變更。為了避免這種問題,Elasticsearch采用了translog。所有索引/刪除/更新操作被寫入到translog,在每個(gè)索引/刪除/更新操作執(zhí)行之后(默認(rèn)情況下是每5秒),translog會被同步以確保變更被持久化。translog被同步到主分片和副本之后,客戶端才會收到寫請求的確認(rèn)。

    在兩次Lucene提交之間發(fā)生硬件故障的情況下,可以通過重放translog來恢復(fù)自最后一次Lucene提交前的任何丟失的變更,所有的變更將會被索引所接受。

    注意:建議在重啟Elasticsearch實(shí)例之前顯式地執(zhí)行沖洗translog,這樣啟動(dòng)會更快,因?yàn)橐胤诺膖ranslog被清空。POST /_all/_flush命令可用于沖洗集群中的所有索引。

    使用translog的沖洗操作,在文件系統(tǒng)緩存中的段被提交到磁盤,使索引中的變更持久化。現(xiàn)在讓我們來看看Lucene的段。

    Lucene的段

    Lucene索引是由多個(gè)段組成,段本身是一個(gè)功能齊全的倒排索引。段是不可變的,允許Lucene將新的文檔增量地添加到索引中,而不用從頭重建索引。對于每一個(gè)搜索請求而言,索引中的所有段都會被搜索,并且每個(gè)段會消耗CPU的時(shí)鐘周、文件句柄和內(nèi)存。這意味著段的數(shù)量越多,搜索性能會越低。

    為了解決這個(gè)問題,Elasticsearch會合并小段到一個(gè)較大的段(如下圖所示),提交新的合并段到磁盤,并刪除那些舊的小段。

    這會在后臺自動(dòng)執(zhí)行而不中斷索引或者搜索。由于段合并會耗盡資源,影響搜索性能,Elasticsearch會節(jié)制合并過程,為搜索提供足夠的可用資源。

    接下來有什么?

    從搜索請求角度來說,一個(gè)Elasticsearch索引中給定分片內(nèi)的所有Lucene段都會被搜索,但是,從Elasticsearch集群角度而言,獲取所有匹配的文檔或者深入有序結(jié)果文檔是有害的。在本系列的后續(xù)文章中我們將揭曉原因,讓我們來看一下接下來的主題,內(nèi)容包括了一些在Elasticsearch中為相關(guān)性搜索結(jié)果的低延遲所做的權(quán)衡。

    • Elasticsearch準(zhǔn)實(shí)時(shí)性方面的內(nèi)容
    • 為什么搜索中的深層分頁是有害的?
    • 搜索相關(guān)性計(jì)算中的權(quán)衡之道

    查看原文地址:Anatomy of an Elasticsearch Cluster: Part II

    posted @ 2016-08-13 11:26 小馬歌 閱讀(213) | 評論 (0)編輯 收藏
     
    from:http://www.infoq.com/cn/articles/anatomy-of-an-elasticsearch-cluster-part03


    剖析Elasticsearch集群系列涵蓋了當(dāng)今最流行的分布式搜索引擎Elasticsearch的底層架構(gòu)和原型實(shí)例。本文是這個(gè)系列的第三篇,我們將討論Elasticsearch是如何提供近實(shí)時(shí)搜索并權(quán)衡搜索相關(guān)性計(jì)算的。

    本系列已經(jīng)得到原文著者Ronak Nathani的授權(quán)

    在本系列的前一篇中,我們討論了Elastisearch如何解決分布式系統(tǒng)中的一些基本挑戰(zhàn)。在本文中,我們將探討Elasticsearch在近實(shí)時(shí)搜索及其權(quán)衡計(jì)算搜索相關(guān)性方面的內(nèi)容,Insight Data的工程師們已經(jīng)在使用Elasticsearch構(gòu)建的數(shù)據(jù)平臺之上,對此有所實(shí)踐。我將在本文中主要講述:

    近實(shí)時(shí)搜索

    雖然Elasticsearch中的變更不能立即可見,它還是提供了一個(gè)近實(shí)時(shí)的搜索引擎。如前一篇中所述,提交Lucene的變更到磁盤是一個(gè)代價(jià)昂貴的操作。為了避免在文檔對查詢依然有效的時(shí)候,提交變更到磁盤,Elasticsearch在內(nèi)存緩沖和磁盤之間提供了一個(gè)文件系統(tǒng)緩存。內(nèi)存緩存(默認(rèn)情況下)每1秒刷新一次,在文件系統(tǒng)緩存中使用倒排索引創(chuàng)建一個(gè)新的段。這個(gè)段是開放的并對搜索有效。

    文件系統(tǒng)緩存可以擁有文件句柄,文件可以是開放的、可讀的或者是關(guān)閉的,但是它存在于內(nèi)存之中。因?yàn)樗⑿麻g隔默認(rèn)是1秒,變更不能立即可見,所以說是近實(shí)時(shí)的。因?yàn)?a style="text-decoration: none; color: #286ab2; outline: none !important; margin: 0px; border: 0px; padding: 0px;">translog是尚未落盤的變更持久化記錄,它能有助于CRUD操作方面的近實(shí)時(shí)性。對于每次請求來說,在查找相關(guān)段之前,任何最近的變更都能從translog搜索到,因此客戶端可以訪問到所有的近實(shí)時(shí)變更。

    你可以在創(chuàng)建/更新/刪除操作后顯式地刷新索引,使變更立即可見,但我并不推薦你這樣做,因?yàn)檫@樣會創(chuàng)建出來非常多的小segment而影響搜索性能。對于每次搜索請求來說,給定Elasticsearch索引分片中的全部Lucene段都會被搜索到,但是,對于Elasticsearch來說,獲取全部匹配的文檔或者很深結(jié)果頁的文檔是有害的。讓我們來一起看看為什么是這樣。

    為什么深層分頁在分布式搜索中是有害的?

    當(dāng)我們的一次搜索請求在Elasticsearch中匹配了很多的文檔,默認(rèn)情況下,返回的第一頁只包含前10條結(jié)果。search API提供了fromsize參數(shù),用于指定對于匹配搜索的全部文檔,要返回多深的結(jié)果。舉例來說,如果我們想看到匹配搜索的文檔中,排名為5060之間的文檔,可以設(shè)置from=50size=10。當(dāng)每個(gè)分片接收到這個(gè)搜索請求后,各自會創(chuàng)建一個(gè)容量為from+size的優(yōu)先隊(duì)列來存儲該分片上的搜索結(jié)果,然后將結(jié)果返回給協(xié)調(diào)節(jié)點(diǎn)。

    如果我們想看到排名為50,00050,010的結(jié)果,那么每個(gè)分片要?jiǎng)?chuàng)建一個(gè)容量為50,010的優(yōu)先隊(duì)列來存儲結(jié)果,而協(xié)調(diào)節(jié)點(diǎn)要在內(nèi)存中對數(shù)量為shards * 50,010的結(jié)果進(jìn)行排序。這個(gè)級別的分頁有可能得到結(jié)果,也有可以無法實(shí)現(xiàn),這取決于我們的硬件資源,但是這足以說明,我們得非常小心地使用深分頁,因?yàn)檫@非常容易使我們的集群崩潰。

    一種獲取全部匹配結(jié)果文檔的可行性方案是使用scroll API,它的角色更像關(guān)系數(shù)據(jù)庫中的游標(biāo)。使用scroll API無法進(jìn)行排序,每個(gè)分片只要有匹配搜索的文檔,就會持續(xù)發(fā)送結(jié)果給協(xié)調(diào)節(jié)點(diǎn)。

    獲取大量文檔的時(shí)候,對結(jié)果進(jìn)行得分排序會非常昂貴。并且由于Elasticsearch是分布式系統(tǒng),為每個(gè)文檔計(jì)算搜索相關(guān)性得分是非常昂貴的。現(xiàn)在,讓我們一起看看計(jì)算搜索相關(guān)性的諸多權(quán)衡中的一種。

    計(jì)算搜索相關(guān)性中的權(quán)衡

    Elasticsearch使用tf-idf來計(jì)算搜索相關(guān)性。由于其分布式的性質(zhì),計(jì)算全局的idf(inverse document frequency,逆文檔頻率)非常昂貴。反之可以這樣,每個(gè)分片計(jì)算本地的idf并將相關(guān)性得分分配給結(jié)果文檔,返回的結(jié)果只關(guān)乎該分片上的文檔。同樣地,所有分片使用本地idf計(jì)算的相關(guān)性得分,返回結(jié)果文檔,協(xié)調(diào)節(jié)點(diǎn)對所有結(jié)果排序并返回前幾條。這樣做在大多數(shù)情況下是沒有問題的,除非索引的關(guān)鍵字詞項(xiàng)有傾斜或者單個(gè)分片上沒有代表全局的足夠數(shù)據(jù)。

    比如說,如果我們搜索“insight”這個(gè)詞,但包含"insight"這個(gè)詞項(xiàng)的大多數(shù)文檔都存放在一個(gè)分片上,這樣以來匹配查詢的文檔將不能公平地在每個(gè)分片上進(jìn)行排序,因?yàn)槊總€(gè)分片上的本地idf的值非常不同,得到的搜索結(jié)果可能不會非常相關(guān)。同樣地,如果沒有足夠的數(shù)據(jù),那么對于某些搜索而言,本地idf的值可能大有不同,結(jié)果也會不如預(yù)期相關(guān)。在有足夠數(shù)據(jù)的真實(shí)場景中,本地idf值一般會趨于均等,搜索結(jié)果是相關(guān)的,因?yàn)槲臋n得到了公平的得分。

    這里有2種應(yīng)對本地idf得分的辦法,但都不建議真正在生產(chǎn)環(huán)境中使用。

    • 一種辦法是一索引一分片,本地idf即是全局idf,但這沒有為并行計(jì)算/水平伸縮留有余地,對于大型索引并不實(shí)用。
    • 另一種辦法是在搜索請求中使用dfs_query_then_search (dfs = distributed frequency search,分布式頻率搜索) 參數(shù),這樣以來,會首先計(jì)算每個(gè)分片的本地idf,然后綜合這些本地idf的值來計(jì)算整個(gè)索引的全局idf值,最后使用全局idf計(jì)算相關(guān)性得分來返回結(jié)果。這種方式不為生產(chǎn)環(huán)境推薦,因?yàn)橛凶銐虻臄?shù)據(jù)確保詞項(xiàng)頻率分布均勻。

    在本系列的過去幾篇中,我們回顧了一些Elasticsearch的基本原則,對于我們理解并上手Elasticsearch,這些內(nèi)容非常重要。在接下來的一篇中,我將使用Apache Spark來研究Elasticsearch中的索引數(shù)據(jù)。

    查看英文原文:Anatomy of an Elasticsearch Cluster: Part III

    posted @ 2016-08-13 11:26 小馬歌 閱讀(260) | 評論 (0)編輯 收藏
     
    from:http://www.infoq.com/cn/articles/analysis-of-elasticsearch-cluster-part01

    剖析Elasticsearch集群系列涵蓋了當(dāng)今最流行的分布式搜索引擎Elasticsearch的底層架構(gòu)和原型實(shí)例。

    本文是這個(gè)系列的第一篇,在本文中,我們將討論的Elasticsearch的底層存儲模型及CRUD(創(chuàng)建、讀取、更新和刪除)操作的工作原理。

    本系列已經(jīng)得到原文著者Ronak Nathani的授權(quán)

    Elasticsearch是當(dāng)今最流行的分布式搜索引擎,GitHub、 SalesforceIQ、Netflix等公司將其用于全文檢索和分析應(yīng)用。在Insight,我們用到了Elasticsearch的諸多不同功能,比如:

    • 全文檢索
      • 比如找到與搜索詞項(xiàng)(term)最相關(guān)的維基百科文章。
    • 聚合
      • 比如在廣告網(wǎng)絡(luò)中,可視化的搜索詞項(xiàng)的競價(jià)直方圖。
    • 地理空間API
      • 比如在順風(fēng)車平臺,匹配最近的司機(jī)和乘客。

    正是因?yàn)镋lasticsearch如此流行并且就在我們身邊,我決定深入研究一下。本文,我將分享Elasticsearch的存儲模型和CRUD操作的工作原理。

    當(dāng)我在思考分布式系統(tǒng)是如何工作時(shí),我腦海里的圖案是這樣的:

    水面以上的是API,以下的才是真正的引擎,一切魔幻般的事件都發(fā)生在水下。本文所關(guān)注的就是水下的部分,我們將關(guān)注:

    • Elasticsearch是主從架構(gòu)還是無主架構(gòu)
    • Elasticsearch的存儲模型是什么樣的
    • Elasticsearch是怎么執(zhí)行寫操作的
    • Elasticsearch是怎么執(zhí)行讀操作的
    • 如何定義搜索結(jié)果的相關(guān)性

    在我們深入這些概念之前,讓我們熟悉下相關(guān)的術(shù)語。

    1 辨析Elasticsearch的索引與Lucene的索引

    Elasticsearch中的索引是組織數(shù)據(jù)的邏輯空間(就好比數(shù)據(jù)庫)。1個(gè)Elasticsearch的索引有1個(gè)或者多個(gè)分片(默認(rèn)是5個(gè))。分片對應(yīng)實(shí)際存儲數(shù)據(jù)的Lucene的索引,分片自身就是一個(gè)搜索引擎。每個(gè)分片有0或者多個(gè)副本(默認(rèn)是1個(gè))。Elasticsearch的索引還包含"type"(就像數(shù)據(jù)庫中的表),用于邏輯上隔離索引中的數(shù)據(jù)。在Elasticsearch的索引中,給定一個(gè)type,它的所有文檔會擁有相同的屬性(就像表的schema)。

    (點(diǎn)擊放大圖像)

    圖a展示了一個(gè)包含3個(gè)分片的Elasticsearch索引,每個(gè)分片擁有1個(gè)副本。這些分片組成了一個(gè)Elasticsearch索引,每個(gè)分片自身是一個(gè)Lucene索引。圖b展示了Elasticsearch索引、分片、Lucene索引和文檔之間的邏輯關(guān)系。

    對應(yīng)于關(guān)系數(shù)據(jù)庫術(shù)語

    Elasticsearch Index == Database  Types == Tables  Properties == Schema

    現(xiàn)在我們熟悉了Elasticsearch世界的術(shù)語,接下來讓我們看一下節(jié)點(diǎn)有哪些不同的角色。

    2 節(jié)點(diǎn)類型

    一個(gè)Elasticsearch實(shí)例是一個(gè)節(jié)點(diǎn),一組節(jié)點(diǎn)組成了集群。Elasticsearch集群中的節(jié)點(diǎn)可以配置為3種不同的角色:

    • 主節(jié)點(diǎn):控制Elasticsearch集群,負(fù)責(zé)集群中的操作,比如創(chuàng)建/刪除一個(gè)索引,跟蹤集群中的節(jié)點(diǎn),分配分片到節(jié)點(diǎn)。主節(jié)點(diǎn)處理集群的狀態(tài)并廣播到其他節(jié)點(diǎn),并接收其他節(jié)點(diǎn)的確認(rèn)響應(yīng)。

      每個(gè)節(jié)點(diǎn)都可以通過設(shè)定配置文件elasticsearch.yml中的node.master屬性為true(默認(rèn))成為主節(jié)點(diǎn)。

      對于大型的生產(chǎn)集群來說,推薦使用一個(gè)專門的主節(jié)點(diǎn)來控制集群,該節(jié)點(diǎn)將不處理任何用戶請求。

    • 數(shù)據(jù)節(jié)點(diǎn):持有數(shù)據(jù)和倒排索引。默認(rèn)情況下,每個(gè)節(jié)點(diǎn)都可以通過設(shè)定配置文件elasticsearch.yml中的node.data屬性為true(默認(rèn))成為數(shù)據(jù)節(jié)點(diǎn)。如果我們要使用一個(gè)專門的主節(jié)點(diǎn),應(yīng)將其node.data屬性設(shè)置為false

    • 客戶端節(jié)點(diǎn):如果我們將node.master屬性和node.data屬性都設(shè)置為false,那么該節(jié)點(diǎn)就是一個(gè)客戶端節(jié)點(diǎn),扮演一個(gè)負(fù)載均衡的角色,將到來的請求路由到集群中的各個(gè)節(jié)點(diǎn)。

    Elasticsearch集群中作為客戶端接入的節(jié)點(diǎn)叫協(xié)調(diào)節(jié)點(diǎn)。協(xié)調(diào)節(jié)點(diǎn)會將客戶端請求路由到集群中合適的分片上。對于讀請求來說,協(xié)調(diào)節(jié)點(diǎn)每次會選擇不同的分片處理請求,以實(shí)現(xiàn)負(fù)載均衡。

    在我們開始研究發(fā)送給協(xié)調(diào)節(jié)點(diǎn)的CRUD請求是如何在集群中傳播并被引擎執(zhí)行之前,讓我們先來看一下Elasticsearch內(nèi)部是如何存儲數(shù)據(jù),以支持全文檢索結(jié)果的低延遲服務(wù)的。

    存儲模型

    Elasticsearch使用了Apache Lucene,后者是Doug Cutting(Apache Hadoop之父)使用Java開發(fā)的全文檢索工具庫,其內(nèi)部使用的是被稱為倒排索引的數(shù)據(jù)結(jié)構(gòu),其設(shè)計(jì)是為全文檢索結(jié)果的低延遲提供服務(wù)。文檔是Elasticsearch的數(shù)據(jù)單位,對文檔中的詞項(xiàng)進(jìn)行分詞,并創(chuàng)建去重詞項(xiàng)的有序列表,將詞項(xiàng)與其在文檔中出現(xiàn)的位置列表關(guān)聯(lián),便形成了倒排索引。

    這和一本書后面的索引非常類似,即書中包含的詞匯與其出現(xiàn)的頁碼列表關(guān)聯(lián)。當(dāng)我們說文檔被索引了,我們指的是倒排索引。我們來看下如下2個(gè)文檔是如何被倒排索引的:

    文檔1(Doc 1): Insight Data Engineering Fellows Program
    文檔2(Doc 2): Insight Data Science Fellows Program

    如果我們想找包含詞項(xiàng)"insight"的文檔,我們可以掃描這個(gè)(單詞有序的)倒排索引,找到"insight"并返回包含改詞的文檔ID,示例中是Doc 1和Doc 2。

    為了提高可檢索性(比如希望大小寫單詞都返回),我們應(yīng)當(dāng)先分析文檔再對其索引。分析包括2個(gè)部分:

    • 將句子詞條化為獨(dú)立的單詞
    • 將單詞規(guī)范化為標(biāo)準(zhǔn)形式

    默認(rèn)情況下,Elasticsearch使用標(biāo)準(zhǔn)分析器,它使用了:

    • 標(biāo)準(zhǔn)分詞器以單詞為界來切詞
    • 小寫詞條(token)過濾器來轉(zhuǎn)換單詞

    還有很多可用的分析器在此不列舉,請參考相關(guān)文檔。

    為了實(shí)現(xiàn)查詢時(shí)能得到對應(yīng)的結(jié)果,查詢時(shí)應(yīng)使用與索引時(shí)一致的分析器,對文檔進(jìn)行分析。

    注意:標(biāo)準(zhǔn)分析器包含了停用詞過濾器,但默認(rèn)情況下沒有啟用。

    現(xiàn)在,倒排索引的概念已經(jīng)清楚,讓我們開始CRUD操作的研究吧。我們從寫操作開始。

    剖析寫操作

    創(chuàng)建((C)reate)

    當(dāng)我們發(fā)送索引一個(gè)新文檔的請求到協(xié)調(diào)節(jié)點(diǎn)后,將發(fā)生如下一組操作:

    • Elasticsearch集群中的每個(gè)節(jié)點(diǎn)都包含了改節(jié)點(diǎn)上分片的元數(shù)據(jù)信息。協(xié)調(diào)節(jié)點(diǎn)(默認(rèn))使用文檔ID參與計(jì)算,以便為路由提供合適的分片。Elasticsearch使用MurMurHash3函數(shù)對文檔ID進(jìn)行哈希,其結(jié)果再對分片數(shù)量取模,得到的結(jié)果即是索引文檔的分片。

      shard = hash(document_id) % (num_of_primary_shards)
    • 當(dāng)分片所在的節(jié)點(diǎn)接收到來自協(xié)調(diào)節(jié)點(diǎn)的請求后,會將該請求寫入translog(我們將在本系列接下來的文章中講到),并將文檔加入內(nèi)存緩沖。如果請求在主分片上成功處理,該請求會并行發(fā)送到該分片的副本上。當(dāng)translog被同步(fsync)到全部的主分片及其副本上后,客戶端才會收到確認(rèn)通知。
    • 內(nèi)存緩沖會被周期性刷新(默認(rèn)是1秒),內(nèi)容將被寫到文件系統(tǒng)緩存的一個(gè)新段上。雖然這個(gè)段并沒有被同步(fsync),但它是開放的,內(nèi)容可以被搜索到。
    • 每30分鐘,或者當(dāng)translog很大的時(shí)候,translog會被清空,文件系統(tǒng)緩存會被同步。這個(gè)過程在Elasticsearch中稱為沖洗(flush)。在沖洗過程中,內(nèi)存中的緩沖將被清除,內(nèi)容被寫入一個(gè)新段。段的fsync將創(chuàng)建一個(gè)新的提交點(diǎn),并將內(nèi)容刷新到磁盤。舊的translog將被刪除并開始一個(gè)新的translog。

    下圖展示了寫請求及其數(shù)據(jù)流。

    (點(diǎn)擊放大圖像)

    更新((U)pdate)和刪除((D)elete)

    刪除和更新也都是寫操作。但是Elasticsearch中的文檔是不可變的,因此不能被刪除或者改動(dòng)以展示其變更。那么,該如何刪除和更新文檔呢?

    磁盤上的每個(gè)段都有一個(gè)相應(yīng)的.del文件。當(dāng)刪除請求發(fā)送后,文檔并沒有真的被刪除,而是在.del文件中被標(biāo)記為刪除。該文檔依然能匹配查詢,但是會在結(jié)果中被過濾掉。當(dāng)段合并(我們將在本系列接下來的文章中講到)時(shí),在.del文件中被標(biāo)記為刪除的文檔將不會被寫入新段。

    接下來我們看更新是如何工作的。在新的文檔被創(chuàng)建時(shí),Elasticsearch會為該文檔指定一個(gè)版本號。當(dāng)執(zhí)行更新時(shí),舊版本的文檔在.del文件中被標(biāo)記為刪除,新版本的文檔被索引到一個(gè)新段。舊版本的文檔依然能匹配查詢,但是會在結(jié)果中被過濾掉。

    文檔被索引或者更新后,我們就可以執(zhí)行查詢操作了。讓我們看看在Elasticsearch中是如何處理查詢請求的。

    剖析讀操作((R)ead)

    讀操作包含2部分內(nèi)容:

    • 查詢階段
    • 提取階段

    我們來看下每個(gè)階段是如何工作的。

    查詢階段

    在這個(gè)階段,協(xié)調(diào)節(jié)點(diǎn)會將查詢請求路由到索引的全部分片(主分片或者其副本)上。每個(gè)分片獨(dú)立執(zhí)行查詢,并為查詢結(jié)果創(chuàng)建一個(gè)優(yōu)先隊(duì)列,以相關(guān)性得分排序(我們將在本系列的后續(xù)文章中講到)。全部分片都將匹配文檔的ID及其相關(guān)性得分返回給協(xié)調(diào)節(jié)點(diǎn)。協(xié)調(diào)節(jié)點(diǎn)創(chuàng)建一個(gè)優(yōu)先隊(duì)列并對結(jié)果進(jìn)行全局排序。會有很多文檔匹配結(jié)果,但是,默認(rèn)情況下,每個(gè)分片只發(fā)送前10個(gè)結(jié)果給協(xié)調(diào)節(jié)點(diǎn),協(xié)調(diào)節(jié)點(diǎn)為全部分片上的這些結(jié)果創(chuàng)建優(yōu)先隊(duì)列并返回前10個(gè)作為hit。

    提取階段

    當(dāng)協(xié)調(diào)節(jié)點(diǎn)在生成的全局有序的文檔列表中,為全部結(jié)果排好序后,它將向包含原始文檔的分片發(fā)起請求。全部分片填充文檔信息并將其返回給協(xié)調(diào)節(jié)點(diǎn)。

    下圖展示了讀請求及其數(shù)據(jù)流。

    (點(diǎn)擊放大圖像)

    如上所述,查詢結(jié)果是按相關(guān)性排序的。接下來,讓我們看看相關(guān)性是如何定義的。

    搜索相關(guān)性

    相關(guān)性是由搜索結(jié)果中Elasticsearch打給每個(gè)文檔的得分決定的。默認(rèn)使用的排序算法是tf/idf(詞頻/逆文檔頻率)。詞頻衡量了一個(gè)詞項(xiàng)在文檔中出現(xiàn)的次數(shù) (頻率越高 == 相關(guān)性越高),逆文檔頻率衡量了詞項(xiàng)在全部索引中出現(xiàn)的頻率,是一個(gè)索引中文檔總數(shù)的百分比(頻率越高 == 相關(guān)性越低)。最后的得分是tf-idf得分與其他因子比如(短語查詢中的)詞項(xiàng)接近度、(模糊查詢中的)詞項(xiàng)相似度等的組合。

    接下來有什么?

    這些CRUD操作由Elasticsearch內(nèi)部的一些數(shù)據(jù)結(jié)構(gòu)所支持,這對于理解Elasticsearch的工作機(jī)制非常重要。在接下來的系列文章中,我將帶大家走進(jìn)類似的那些概念并告訴大家在使用Elasticsearch中有哪些坑。

    • Elasticsearch中的腦裂問題及防治措施
    • 事務(wù)日志
    • Lucene的段
    • 為什么搜索時(shí)使用深層分頁很危險(xiǎn)
    • 計(jì)算搜索相關(guān)性中困難及權(quán)衡
    • 并發(fā)控制
    • 為什么Elasticsearch是準(zhǔn)實(shí)時(shí)的
    • 如何確保讀和寫的一致性

    查看原文地址:http://insightdataengineering.com/blog/elasticsearch-crud

    posted @ 2016-08-13 11:15 小馬歌 閱讀(255) | 評論 (0)編輯 收藏
     
    from:http://www.infoq.com/cn/articles/what-complete-micro-service-system-should-include?utm_source=infoq&utm_medium=popular_widget&utm_campaign=popular_content_list&utm_content=homepage


    近幾年,微服務(wù)架構(gòu)迅速在整個(gè)技術(shù)社區(qū)竄紅,它被認(rèn)為是IT軟件架構(gòu)的未來方向,大神Martin Fowler也給微服務(wù)極高的評價(jià)。那為什么我們需要微服務(wù),微服務(wù)的真正優(yōu)勢到底是什么,一個(gè)完整的微服務(wù)系統(tǒng),應(yīng)該包含哪些功能,本文作者劉彥夫在軟件設(shè)計(jì)和開發(fā)領(lǐng)域有10多年工作經(jīng)驗(yàn),他將會從他的角度給出答案。

    對微服務(wù)的基本理解

    顧名思義,微服務(wù)要從兩個(gè)方面來理解,一個(gè)是“微”,一個(gè)是“服務(wù)”。體型小到一定程度才能叫“微”,這個(gè)程度是什么呢?一個(gè)身高1米6,體重90斤的MM,我們說她苗條。微服務(wù)也一樣,根據(jù)亞馬遜CEO Bezos給出的有趣定義,單個(gè)微服務(wù)的設(shè)計(jì)、開發(fā)、測試和運(yùn)維的所有人加在一起吃飯,只需要兩個(gè)批薩就夠了,這是就是著名的two pizza team rule。

    具備什么樣的能力才能算是“服務(wù)”?這個(gè)話題很大,我這里按照自己的片面理解總結(jié)一下,所謂服務(wù)就一定會區(qū)別于系統(tǒng)的功能,服務(wù)是一個(gè)或者一組相對的較小且獨(dú)立的功能單元,是用戶可以感知的功能最小集,比如:購物車,訂單,信用卡結(jié)算等都可以作為單個(gè)服務(wù)獨(dú)立提供。

    這個(gè)理解顯然不夠深刻,為了進(jìn)一步理解為什么微服務(wù)在近兩年業(yè)界迅速竄紅,理解為什么微服務(wù)會被認(rèn)為是IT軟件架構(gòu)的未來方向,就要理解為什么我們需要微服務(wù)?它能給企業(yè)帶來什么價(jià)值。傳統(tǒng)企業(yè)的IT軟件大多都是各種獨(dú)立系統(tǒng)的堆砌,這些系統(tǒng)的問題總結(jié)來說就是擴(kuò)展性差,可靠性不高,維護(hù)成本高。后來有了一個(gè)叫SOA的軟件架構(gòu)專門針對這些問題給出了一套解決方案,很多企業(yè)也因此將自身IT系統(tǒng)遷移到SOA架構(gòu)上。

    但是,由于SOA早期均使用了總線模式,這種總線模式是與某種技術(shù)棧強(qiáng)綁定的,比如:J2EE。這導(dǎo)致很多企業(yè)的遺留系統(tǒng)很難對接,切換時(shí)間太長,成本太高,新系統(tǒng)穩(wěn)定性的收斂也需要一些時(shí)間。最終SOA開起來很美,但卻成為了企業(yè)級奢侈品,中小公司都望而生畏。

    依然SOA

    微服務(wù),從本質(zhì)意義上看,還是SOA架構(gòu)。但內(nèi)涵有所不同,微服務(wù)并不綁定某種特殊的技術(shù),在一個(gè)微服務(wù)的系統(tǒng)中,可以有Java編寫的服務(wù),也可以有Python編寫的服務(wù),他們是靠Restful架構(gòu)風(fēng)格統(tǒng)一成一個(gè)系統(tǒng)的。

    最粗淺的理解就是將微服務(wù)之間的交互看作是各種字符串的傳遞,各種語言都可以很好的處理字符串,所以微服務(wù)本身與具體技術(shù)實(shí)現(xiàn)無關(guān),擴(kuò)展性強(qiáng)。另一個(gè)不同是微服務(wù)架構(gòu)本身很輕,底層也有類似于SOA的總線,不過非常輕薄,現(xiàn)在看到的就兩種方式:MQ和HTTP,而HTTP都不能完全等同于總線,而僅僅是個(gè)信息通道。

    所以,基于這種簡單的的協(xié)議規(guī)范,無論是兼容老舊系統(tǒng),還是上線新業(yè)務(wù),都可以隨著時(shí)代的步伐,滾動(dòng)升級。比如:你去年還在使用.NET技術(shù),今年就可以平滑的過度到Go了,而且系統(tǒng)已有服務(wù)不用改動(dòng)。所以微服務(wù)架構(gòu),既保護(hù)用戶已有投資,又很容易向新技術(shù)演進(jìn)。

    微服務(wù)水下的冰山

    人月不是銀彈,微服務(wù)更不是銀彈,好像軟件微服務(wù)化了,軟件系統(tǒng)就能夠應(yīng)對各種問題了。其實(shí)微服務(wù)的水面下藏著巨大的冰山。下面是微服務(wù)提供的能力,以及背后需要付出的代價(jià)。

    1. 單個(gè)微服務(wù)代碼量小,易修改和維護(hù)。但是,系統(tǒng)復(fù)雜度的總量是不變的,每個(gè)服務(wù)代碼少了,但服務(wù)的個(gè)數(shù)肯定就多了。就跟拼圖游戲一樣,切的越碎,越難拼出整幅圖。一個(gè)系統(tǒng)被拆分成零碎的微服務(wù),最后要集成為一個(gè)完整的系統(tǒng),其復(fù)雜度肯定比大塊的功能集成要高很多。

    2. 單個(gè)微服務(wù)數(shù)據(jù)獨(dú)立,可獨(dú)立部署和運(yùn)行。雖然微服務(wù)本身是可以獨(dú)立部署和運(yùn)行的,但仍然避免不了業(yè)務(wù)上的你來我往,這就涉及到要對外通信,當(dāng)微服務(wù)的數(shù)量達(dá)到一定量級的時(shí)候,如何提供一個(gè)高效的集群通信機(jī)制成為一個(gè)問題。

    3. 單個(gè)微服務(wù)擁有自己的進(jìn)程,進(jìn)程本身就可以動(dòng)態(tài)的啟停,為無縫升級的打好了基礎(chǔ),但誰來啟動(dòng)和停止進(jìn)程,什么時(shí)機(jī),選擇在哪臺設(shè)備上做這件事情才是無縫升級的關(guān)鍵。這個(gè)能力并不是微服務(wù)本身提供的,而是需要背后強(qiáng)大的版本管理和部署能力。

    4. 多個(gè)相同的微服務(wù)可以做負(fù)載均衡,提高性能和可靠性。正是因?yàn)橄嗤⒎?wù)可以有多個(gè)不同實(shí)例,讓服務(wù)按需動(dòng)態(tài)伸縮成為可能,在高峰期可以啟動(dòng)更多的相同的微服務(wù)實(shí)例為更多用戶服務(wù),以此提高響應(yīng)速度。同時(shí)這種機(jī)制也提供了高可靠性,在某個(gè)微服務(wù)故障后,其他相同的微服務(wù)可以接替其工作,對外表現(xiàn)為某個(gè)設(shè)備故障后業(yè)務(wù)不中斷。同樣的道理,微服務(wù)本身是不會去關(guān)心系統(tǒng)負(fù)載的,那么什么時(shí)候應(yīng)該啟動(dòng)更多的微服務(wù),多個(gè)微服務(wù)的流量應(yīng)該如何調(diào)度和分發(fā),這背后也有一套復(fù)雜的負(fù)載監(jiān)控和均衡的系統(tǒng)在起作用。

    5. 微服務(wù)可以獨(dú)立部署和對外提供服務(wù),微服務(wù)的業(yè)務(wù)上線和下線是動(dòng)態(tài)的,當(dāng)一個(gè)新的微服務(wù)上線時(shí),用戶是如何訪問到這種新的服務(wù)?這就需要有一個(gè)統(tǒng)一的入口,新的服務(wù)可以動(dòng)態(tài)的注冊到這個(gè)入口上,用戶每次訪問時(shí)可以從這個(gè)入口拿到系統(tǒng)所有服務(wù)的訪問地址,類似于到餐廳吃飯,新菜要寫到“菜單”中,以供用戶選擇。這個(gè)統(tǒng)一的系統(tǒng)入口并不是微服務(wù)本身的一部分,所以這種能力需要系統(tǒng)單獨(dú)提供。

    6. 還有一些企業(yè)級關(guān)注的系統(tǒng)問題,比如,安全策略如何集中管理?系統(tǒng)故障如何快速審計(jì)和跟蹤到具體服務(wù)?整個(gè)系統(tǒng)狀態(tài)如何監(jiān)控?服務(wù)之間的依賴關(guān)系如何管理?等等這些問題都不是單個(gè)微服務(wù)考慮的范疇,而需要有一個(gè)系統(tǒng)性的考慮和設(shè)計(jì),讓每個(gè)微服務(wù)都能夠按照系統(tǒng)性的要求和約束提供對應(yīng)的安全性,可靠性,可維護(hù)性的能力。

    綜上所述,微服務(wù)關(guān)鍵其實(shí)不僅僅是微服務(wù)本身,而是系統(tǒng)要提供一套基礎(chǔ)的架構(gòu),這種架構(gòu)使得微服務(wù)可以獨(dú)立的部署、運(yùn)行、升級,不僅如此,這個(gè)系統(tǒng)架構(gòu)還讓微服務(wù)與微服務(wù)之間在結(jié)構(gòu)上“松耦合”,而在功能上則表現(xiàn)為一個(gè)統(tǒng)一的整體。這種所謂的“統(tǒng)一的整體”表現(xiàn)出來的是統(tǒng)一風(fēng)格的界面,統(tǒng)一的權(quán)限管理,統(tǒng)一的安全策略,統(tǒng)一的上線過程,統(tǒng)一的日志和審計(jì)方法,統(tǒng)一的調(diào)度方式,統(tǒng)一的訪問入口等等。

    這些系統(tǒng)性的功能也需要有一些服務(wù)來提供,這些服務(wù)不會直接呈現(xiàn)給最終用戶,也就是微服務(wù)系統(tǒng)冰山下面的部分,我們可以簡稱它為微服務(wù)系統(tǒng)的“底座”。所有的微服務(wù)都像一個(gè)APP,插在這個(gè)底座的上面,享受這個(gè)底座提供的系統(tǒng)能力比如:元數(shù)據(jù)存放、灰度發(fā)布、藍(lán)綠部署等等。

    微服務(wù)系統(tǒng)底座

    一個(gè)完整的微服務(wù)系統(tǒng),它的底座最少要包含以下功能:

    • 日志和審計(jì),主要是日志的匯總,分類和查詢

    • 監(jiān)控和告警,主要是監(jiān)控每個(gè)服務(wù)的狀態(tài),必要時(shí)產(chǎn)生告警

    • 消息總線,輕量級的MQ或HTTP

    • 注冊發(fā)現(xiàn)

    • 負(fù)載均衡

    • 部署和升級

    • 事件調(diào)度機(jī)制

    • 資源管理,如:底層的虛擬機(jī),物理機(jī)和網(wǎng)絡(luò)管理

    以下功能不是最小集的一部分,但也屬于底座功能:

    • 認(rèn)證和鑒權(quán)

    • 微服務(wù)統(tǒng)一代碼框架,支持多種編程語言

    • 統(tǒng)一服務(wù)構(gòu)建和打包

    • 統(tǒng)一服務(wù)測試

    • 微服務(wù)CI/CD流水線

    • 服務(wù)依賴關(guān)系管理

    • 統(tǒng)一問題跟蹤調(diào)試框架,俗稱調(diào)用鏈

    • 灰度發(fā)布

    • 藍(lán)綠部署

      • 令人困惑的幾個(gè)問題

      微服務(wù)的底座是不是必須的?

      是的,基本上是必須的。你可以不用代碼實(shí)現(xiàn)一個(gè)資源管理服務(wù),可以手工用Excel管理你的所有機(jī)器資源,但是不代表微服務(wù)系統(tǒng)沒有這個(gè)功能,只不過這個(gè)功能是人工實(shí)現(xiàn)的。再舉個(gè)例子,日志系統(tǒng)如果只是簡單的打印文件,那么多個(gè)微服務(wù)的日志就需要手工收集,人工分類和篩選。所以,微服務(wù)的底座最小集一定會存在,問題是看怎樣實(shí)現(xiàn)它。

      這里僅僅是總結(jié)了對微服務(wù)系統(tǒng)的基本理解,而實(shí)現(xiàn)這個(gè)架構(gòu)有很多技術(shù),這里不進(jìn)行詳細(xì)展開。實(shí)踐方面,推薦王磊的《微服務(wù)架構(gòu)與實(shí)踐》,他描述了使用Ruby相關(guān)的技術(shù)實(shí)現(xiàn)了一整套微服務(wù)系統(tǒng),特別是書中后面的實(shí)踐部分講解了如何將已有的系統(tǒng)演化為微服務(wù)架構(gòu),是很好的參考和指導(dǎo)材料。

      是不是所有軟件都能做微服務(wù)?

      這個(gè)命題有些微妙,也很難說清楚,回答這個(gè)命題本身就是一種挑戰(zhàn),可能最終也沒有正確答案。不過,我還是把我自己的理解寫在這里,讓大家去拍磚。在我這里,答案是否定的。我只需舉出一個(gè)反例,比如:存儲系統(tǒng),其架構(gòu)是傳統(tǒng)的分層架構(gòu),每一層都使用下面一層的服務(wù),并為上一層提供服務(wù)。雖然可以將這種架構(gòu)調(diào)整為基于服務(wù)的架構(gòu),但沒辦法做成微服務(wù)。

      區(qū)別在哪里呢?核心的區(qū)別在于獨(dú)立性上,微服務(wù)大多是可以獨(dú)立的運(yùn)行和使用的,而存儲這種非常底層和基礎(chǔ)的系統(tǒng),每層部件都不能單獨(dú)被使用,比如:Pool管理、CHUNK管理、VOL管理、NFS文件系統(tǒng),這些功能都無法離開另外一些功能而獨(dú)立運(yùn)行,要對外提供可用的存儲功能,一大堆功能必須一起上。這種系統(tǒng)做到極致,最多也就能夠使其部件可以獨(dú)立的部署和升級,俗稱打熱補(bǔ)丁。

      這也就是為什么這種底層傳統(tǒng)系統(tǒng)架構(gòu)通常是單塊架構(gòu)的原因。由于單塊架構(gòu)的各個(gè)部分調(diào)用關(guān)系緊密,做成微服務(wù)后系統(tǒng)集成成本會大大增加,不僅如此,這樣的架構(gòu)做成微服務(wù)并不能提高交付效率,因?yàn)楦鱾€(gè)部分根本就無法獨(dú)立的運(yùn)行和測試。

      什么樣的軟件做成微服務(wù)?

      能不能做成微服務(wù),取決于四個(gè)要素:

      • 小:微服務(wù)體積小,2 pizza團(tuán)隊(duì)。

      • 獨(dú):能夠獨(dú)立的部署和運(yùn)行。

      • 輕:使用輕量級的通信機(jī)制和架構(gòu)。

      • 松:為服務(wù)之間是松耦合的。

      針對于小、輕、松都是可以通過某些技術(shù)手段達(dá)到目的,而獨(dú)立的部署和運(yùn)行,則是和業(yè)務(wù)本身有關(guān)系,如果你這個(gè)系統(tǒng)提供的業(yè)務(wù)是貼近最終用戶的,并且這些功能之間的耦合性很小,則微服務(wù)就可以按照業(yè)務(wù)功能本身的獨(dú)立性來劃分,則這類系統(tǒng)做成微服務(wù)是非常合適的。如果系統(tǒng)提供的業(yè)務(wù)是非常底層的,如:操作系統(tǒng)內(nèi)核、存儲系統(tǒng)、網(wǎng)絡(luò)系統(tǒng)、數(shù)據(jù)庫系統(tǒng)等等,這類系統(tǒng)都偏底層,功能和功能之間有著緊密的配合關(guān)系,如果強(qiáng)制拆分為較小的服務(wù)單元,會讓集成工作量急劇上升,并且這種人為的切割無法帶來業(yè)務(wù)上的真正的隔離,所以無法做到獨(dú)立部署和運(yùn)行,也就更加無法做到真正的微服務(wù)了。


      感謝郭蕾對本文的審校。

      給InfoQ中文站投稿或者參與內(nèi)容翻譯工作,請郵件至editors@cn.infoq.com。也歡迎大家通過新浪微博(@InfoQ@丁曉昀),微信(微信號:InfoQChina)關(guān)注我們。

      posted @ 2016-08-13 10:53 小馬歌 閱讀(233) | 評論 (0)編輯 收藏
       
      作者:MagiLu
      鏈接:https://zhuanlan.zhihu.com/p/20308548
      來源:知乎
      著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。

      作者:johnczchen

      出品:QQ空間終端開發(fā)團(tuán)隊(duì)

      原文發(fā)布于QQ空間終端開發(fā)團(tuán)隊(duì)的官方公眾號,任何形式的轉(zhuǎn)載之前必須與本人聯(lián)系。


      1.背景

      當(dāng)一個(gè)App發(fā)布之后,突然發(fā)現(xiàn)了一個(gè)嚴(yán)重bug需要進(jìn)行緊急修復(fù),這時(shí)候公司各方就會忙得焦頭爛額:重新打包App、測試、向各個(gè)應(yīng)用市場和渠道換包、提示用戶升級、用戶下載、覆蓋安裝。有時(shí)候僅僅是為了修改了一行代碼,也要付出巨大的成本進(jìn)行換包和重新發(fā)布。

      這時(shí)候就提出一個(gè)問題:有沒有辦法以補(bǔ)丁的方式動(dòng)態(tài)修復(fù)緊急Bug,不再需要重新發(fā)布App,不再需要用戶重新下載,覆蓋安裝?

      雖然Android系統(tǒng)并沒有提供這個(gè)技術(shù),但是很幸運(yùn)的告訴大家,答案是:可以,我們QQ空間提出了熱補(bǔ)丁動(dòng)態(tài)修復(fù)技術(shù)來解決以上這些問題。

      2.實(shí)際案例

      空間Android獨(dú)立版5.2發(fā)布后,收到用戶反饋,結(jié)合版無法跳轉(zhuǎn)到獨(dú)立版的訪客界面,每天都較大的反饋。在以前只能緊急換包,重新發(fā)布。成本非常高,也影響用戶的口碑。最終決定使用熱補(bǔ)丁動(dòng)態(tài)修復(fù)技術(shù),向用戶下發(fā)Patch,在用戶無感知的情況下,修復(fù)了外網(wǎng)問題,取得非常好的效果。

      3.解決方案

      該方案基于的是android dex分包方案的,關(guān)于dex分包方案,網(wǎng)上有幾篇解釋了,所以這里就不再贅述,具體可以看這里Android dex分包方案

      簡單的概括一下,就是把多個(gè)dex文件塞入到app的classloader之中,但是android dex拆包方案中的類是沒有重復(fù)的,如果classes.dex和classes1.dex中有重復(fù)的類,當(dāng)用到這個(gè)重復(fù)的類的時(shí)候,系統(tǒng)會選擇哪個(gè)類進(jìn)行加載呢?

      讓我們來看看類加載的代碼:

      一個(gè)ClassLoader可以包含多個(gè)dex文件,每個(gè)dex文件是一個(gè)Element,多個(gè)dex文件排列成一個(gè)有序的數(shù)組dexElements,當(dāng)找類的時(shí)候,會按順序遍歷dex文件,然后從當(dāng)前遍歷的dex文件中找類,如果找類則返回,如果找不到從下一個(gè)dex文件繼續(xù)查找。

      理論上,如果在不同的dex中有相同的類存在,那么會優(yōu)先選擇排在前面的dex文件的類,如下圖:

      在此基礎(chǔ)上,我們構(gòu)想了熱補(bǔ)丁的方案,把有問題的類打包到一個(gè)dex(patch.dex)中去,然后把這個(gè)dex插入到Elements的最前面,如下圖:

      好,該方案基于第二個(gè)拆分dex的方案,方案實(shí)現(xiàn)如果懂拆分dex的原理的話,大家應(yīng)該很快就會實(shí)現(xiàn)該方案,如果沒有拆分dex的項(xiàng)目的話,可以參考一下谷歌的multidex方案實(shí)現(xiàn)。然后在插入數(shù)組的時(shí)候,把補(bǔ)丁包插入到最前面去。

      好,看似問題很簡單,輕松的搞定了,讓我們來試驗(yàn)一下,修改某個(gè)類,然后打包成dex,插入到classloader,當(dāng)加載類的時(shí)候出現(xiàn)了(本例中是QzoneActivityManager要被替換):

      為什么會出現(xiàn)以上問題呢?

      從log的意思上來講,ModuleManager引用了QzoneActivityManager,但是發(fā)現(xiàn)這這兩個(gè)類所在的dex不在一起,其中:

      1. ModuleManager在classes.dex中

      2. QzoneActivityManager在patch.dex中

      結(jié)果發(fā)生了錯(cuò)誤。

      這里有個(gè)問題,拆分dex的很多類都不是在同一個(gè)dex內(nèi)的,怎么沒有問題?

      讓我們搜索一下拋出錯(cuò)誤的代碼所在,嘿咻嘿咻,找到了一下代碼:

      從代碼上來看,如果兩個(gè)相關(guān)聯(lián)的類在不同的dex中就會報(bào)錯(cuò),但是拆分dex沒有報(bào)錯(cuò)這是為什么,原來這個(gè)校驗(yàn)的前提是:

      如果引用者(也就是ModuleManager)這個(gè)類被打上了CLASS_ISPREVERIFIED標(biāo)志,那么就會進(jìn)行dex的校驗(yàn)。那么這個(gè)標(biāo)志是什么時(shí)候被打上去的?

      讓我們在繼續(xù)搜索一下代碼,嘿咻嘿咻~~,在DexPrepare.cpp找到了一下代碼:

      這段代碼是dex轉(zhuǎn)化成odex(dexopt)的代碼中的一段,我們知道當(dāng)一個(gè)apk在安裝的時(shí)候,apk中的classes.dex會被虛擬機(jī)(dexopt)優(yōu)化成odex文件,然后才會拿去執(zhí)行。

      虛擬機(jī)在啟動(dòng)的時(shí)候,會有許多的啟動(dòng)參數(shù),其中一項(xiàng)就是verify選項(xiàng),當(dāng)verify選項(xiàng)被打開的時(shí)候,上面doVerify變量為true,那么就會執(zhí)行dvmVerifyClass進(jìn)行類的校驗(yàn),如果dvmVerifyClass校驗(yàn)類成功,那么這個(gè)類會被打上CLASS_ISPREVERIFIED的標(biāo)志,那么具體的校驗(yàn)過程是什么樣子的呢?

      此代碼在DexVerify.cpp中,如下:

      1. 驗(yàn)證clazz->directMethods方法,directMethods包含了以下方法:

      1. static方法

      2. private方法

      3. 構(gòu)造函數(shù)

      2. clazz->virtualMethods

      1. 虛函數(shù)=override方法?

      概括一下就是如果以上方法中直接引用到的類(第一層級關(guān)系,不會進(jìn)行遞歸搜索)和clazz都在同一個(gè)dex中的話,那么這個(gè)類就會被打上CLASS_ISPREVERIFIED

      所以為了實(shí)現(xiàn)補(bǔ)丁方案,所以必須從這些方法中入手,防止類被打上CLASS_ISPREVERIFIED標(biāo)志。

      最終空間的方案是往所有類的構(gòu)造函數(shù)里面插入了一段代碼,代碼如下:

      if (ClassVerifier.PREVENT_VERIFY) {

      System.out.println(AntilazyLoad.class);

      }

      其中AntilazyLoad類會被打包成單獨(dú)的hack.dex,這樣當(dāng)安裝apk的時(shí)候,classes.dex內(nèi)的類都會引用一個(gè)在不相同dex中的AntilazyLoad類,這樣就防止了類被打上CLASS_ISPREVERIFIED的標(biāo)志了,只要沒被打上這個(gè)標(biāo)志的類都可以進(jìn)行打補(bǔ)丁操作。

      然后在應(yīng)用啟動(dòng)的時(shí)候加載進(jìn)來.AntilazyLoad類所在的dex包必須被先加載進(jìn)來,不然AntilazyLoad類會被標(biāo)記為不存在,即使后續(xù)加載了hack.dex包,那么他也是不存在的,這樣屏幕就會出現(xiàn)茫茫多的類AntilazyLoad找不到的log。

      所以Application作為應(yīng)用的入口不能插入這段代碼。(因?yàn)檩d入hack.dex的代碼是在Application中onCreate中執(zhí)行的,如果在Application的構(gòu)造函數(shù)里面插入了這段代碼,那么就是在hack.dex加載之前就使用該類,該類一次找不到,會被永遠(yuǎn)的打上找不到的標(biāo)志)

      其中:

      之所以選擇構(gòu)造函數(shù)是因?yàn)樗辉黾臃椒〝?shù),一個(gè)類即使沒有顯式的構(gòu)造函數(shù),也會有一個(gè)隱式的默認(rèn)構(gòu)造函數(shù)。

      空間使用的是在字節(jié)碼插入代碼,而不是源代碼插入,使用的是javaassist庫來進(jìn)行字節(jié)碼插入的。

      隱患:

      虛擬機(jī)在安裝期間為類打上CLASS_ISPREVERIFIED標(biāo)志是為了提高性能的,我們強(qiáng)制防止類被打上標(biāo)志是否會影響性能?這里我們會做一下更加詳細(xì)的性能測試.但是在大項(xiàng)目中拆分dex的問題已經(jīng)比較嚴(yán)重,很多類都沒有被打上這個(gè)標(biāo)志。

      如何打包補(bǔ)丁包:

      1. 空間在正式版本發(fā)布的時(shí)候,會生成一份緩存文件,里面記錄了所有class文件的md5,還有一份mapping混淆文件。

      2. 在后續(xù)的版本中使用-applymapping選項(xiàng),應(yīng)用正式版本的mapping文件,然后計(jì)算編譯完成后的class文件的md5和正式版本進(jìn)行比較,把不相同的class文件打包成補(bǔ)丁包。

      備注:該方案現(xiàn)在也應(yīng)用到我們的編譯過程當(dāng)中,編譯不需要重新打包dex,只需要把修改過的類的class文件打包成patch dex,然后放到sdcard下,那么就會讓改變的代碼生效。



      關(guān)于Qzone :

      Qzone 是中國最大的社交網(wǎng)絡(luò),是騰訊集團(tuán)的核心平臺之一,目前Qzone的月活躍賬戶數(shù)達(dá)到6.68億,Qzone智能終端月活躍賬戶數(shù)達(dá)到5.68億。從2005~2015,Qzone見證了國內(nèi)互聯(lián)網(wǎng)蓬勃發(fā)展的十年,這十年風(fēng)云變幻,但我們的業(yè)務(wù)卻不斷向前發(fā)展,也希望更多的朋友能夠加入我們,共同迎接互聯(lián)網(wǎng)和Qzone的下一個(gè)十年。

      posted @ 2016-07-07 16:11 小馬歌 閱讀(232) | 評論 (0)編輯 收藏
      僅列出標(biāo)題
      共95頁: First 上一頁 2 3 4 5 6 7 8 9 10 下一頁 Last 
       
      主站蜘蛛池模板: 亚洲国产V高清在线观看| 一个人看的免费视频www在线高清动漫 | 精品久久久久久亚洲综合网| 四虎免费大片aⅴ入口| 亚洲一区二区无码偷拍| 午夜视频免费成人| 亚洲爆乳成av人在线视菜奈实| 啦啦啦手机完整免费高清观看 | 国产高清在线精品免费软件| 亚洲AV日韩综合一区| 国产精品免费看久久久久| 豆国产96在线|亚洲| 亚洲国产成人久久综合碰| 一个人看www免费高清字幕| 久久久久亚洲AV成人网| 中国性猛交xxxxx免费看| 亚洲成av人片天堂网| 蜜桃视频在线观看免费视频网站WWW| 亚洲一区二区三区日本久久九| 999久久久免费精品播放| 亚洲国产精品xo在线观看| 精品香蕉在线观看免费| 亚洲一区二区三区成人网站| 日韩一区二区免费视频| 羞羞漫画在线成人漫画阅读免费| 亚洲国产V高清在线观看| 两个人看的www免费| 4480yy私人影院亚洲| 无码永久免费AV网站| 亚洲AV无码国产剧情| 亚洲成a人片在线观看国产| 国产无遮挡色视频免费观看性色| 亚洲av无码无在线观看红杏| 8888四色奇米在线观看免费看| 精品日韩99亚洲的在线发布| 国产精品无码免费视频二三区| 色哟哟国产精品免费观看| 国产成人亚洲综合无码精品| 色影音免费色资源| 亚洲成AV人片高潮喷水| MM131亚洲国产美女久久|