大型分布式業(yè)務(wù)平臺數(shù)據(jù)庫優(yōu)化方法(下)

微信版的鏈接地址

文章摘要:當單表數(shù)據(jù)達到千萬以上時,通過加索引或者表分區(qū)優(yōu)化提升的效果就比較有限了忧勿,應(yīng)該如何應(yīng)對呢杉女?瞻讽??

當MySQL數(shù)據(jù)庫的單表數(shù)據(jù)量達到千萬級別以上時熏挎,不管是業(yè)務(wù)邏輯的查詢速勇,還是更新,或者刪除都會使得數(shù)據(jù)庫的平均響應(yīng)時間過長坎拐。這時再通過(上)篇中的單表SQL優(yōu)化技術(shù)解決方案收效就微乎其微了烦磁。對于如此大量數(shù)據(jù),我們還可以利用以下幾種業(yè)務(wù)平臺的架構(gòu)方案進行進一步的技術(shù)改造哼勇。

一个初、分離熱點數(shù)據(jù)方案

當單庫數(shù)據(jù)量比較大影響了查詢/更新/刪除的SQL執(zhí)行效率時,我們可以直接想到在不影響業(yè)務(wù)邏輯的前提下猴蹂,如果可以直接減少數(shù)據(jù)庫中單表的數(shù)據(jù)量,那就能夠達到我們的優(yōu)化數(shù)據(jù)庫的目標楣嘁。該種方案稱之為“分離熱點數(shù)據(jù)”磅轻。

基于最近時間段內(nèi)寫入數(shù)據(jù)一般會成為熱點數(shù)據(jù)的假設(shè),同時考慮到在業(yè)務(wù)平臺中對于存量歷史數(shù)據(jù)的需求基本都是查詢逐虚,而對于平臺中的最近時段生成的數(shù)據(jù)一般會涉及查詢/更新/刪除等操作聋溜。因此可以設(shè)定一個時間的閥值,比如6個月叭爱,根據(jù)這個時間點來進行分表撮躁。對于6個月以內(nèi)的數(shù)據(jù),存熱點庫的數(shù)據(jù)表买雾,6個月以上的數(shù)據(jù)存在歷史庫數(shù)據(jù)表里把曼,業(yè)務(wù)平臺的應(yīng)用工程通過配置多數(shù)據(jù)源以及增加代理層即可實現(xiàn)服務(wù)對于不同數(shù)據(jù)庫的訪問,用以區(qū)分對熱點數(shù)據(jù)庫和歷史庫的訪問漓穿。由于歷史存量數(shù)據(jù)會越來越多嗤军,因此可以根據(jù)業(yè)務(wù)需要對歷史庫內(nèi)的數(shù)據(jù)進行實現(xiàn)MYSQL分區(qū)表。而業(yè)務(wù)平臺遷移歷史數(shù)據(jù)可以在業(yè)務(wù)平臺訪問壓力和流量較小的午夜通過設(shè)置Quartz定時任務(wù)程序的方式執(zhí)行遷移晃危。該種業(yè)務(wù)平臺架構(gòu)方案圖如下:

二叙赚、采用本地或分布式緩存方案

為了緩解數(shù)據(jù)庫單庫單表的IO壓力,盡可能地降低數(shù)據(jù)庫操作(CRUD)平均響應(yīng)時間僚饭。我們可以采用本地緩存或者分布式緩存的技術(shù)方案為DB緩解讀寫壓力震叮。

這里的本地緩存主要指通過利用JDK原有的諸如HashMap/ConcruuentHashMap數(shù)據(jù)結(jié)構(gòu)或者Google guava包中的localCache在應(yīng)用服務(wù)器內(nèi)部構(gòu)建的一塊緩存區(qū)域。分布式緩存即指redis鳍鸵、mencached這一類的緩存中間件(限于篇幅和主題苇瓣,對于這兩類緩存深度技術(shù)和應(yīng)用優(yōu)化的介紹將在后續(xù)的篇幅中會單獨介紹)。

對于業(yè)務(wù)平臺中的局數(shù)據(jù)(比如权纤,資源規(guī)格钓简、類型乌妒、價格、模板等)外邓,該類數(shù)據(jù)從寫入至數(shù)據(jù)表后就極少改動撤蚊,其業(yè)務(wù)需求通常是只讀∷鸹埃考慮到該特點侦啸,可以將這一部分數(shù)據(jù)在應(yīng)用服務(wù)啟動時就放入本地/分布式緩存中,服務(wù)需要讀取時直接從緩存中讀取丧枪,這樣即可在一定程度上減少對數(shù)據(jù)庫讀的壓力光涂。

而對于另外一部分的業(yè)務(wù)數(shù)據(jù)(比如訂單、資源拧烦、性能忘闻、用戶賬戶),往往涉及新增/修改/刪除等對數(shù)據(jù)庫寫的操作恋博,且在一些業(yè)務(wù)場景中(比如大促/營銷活動)這部分數(shù)據(jù)經(jīng)常容易成為熱點數(shù)據(jù)齐佳。因此可以考慮在數(shù)據(jù)庫并發(fā)量較大的情況下,用分布式緩存做為緩沖债沮,在緩存中先完成數(shù)據(jù)的匯總以及其他業(yè)務(wù)邏輯操作炼吴,然后使用批量插入/更新/刪除的方式對數(shù)據(jù)庫完成一次或若干次的批處理操作。這樣可以在保證平臺業(yè)務(wù)不受影響的同時疫衩,減輕數(shù)據(jù)庫的寫壓力硅蹦。下圖即為熱點數(shù)據(jù)緩沖匯總統(tǒng)計的示意圖:

三、分庫分表

1.分庫分表的必要性

當數(shù)據(jù)庫中一張單表的數(shù)據(jù)量達到幾千萬時闷煤,且還有不斷增長趨勢童芹,同時系統(tǒng)的并發(fā)訪問量達到一定規(guī)模的時,前面篇幅中介紹的數(shù)據(jù)表的B-Tree/B+ Tree索引就無法起作用了曹傀。除非是索引覆蓋查詢辐脖,否則數(shù)據(jù)庫服務(wù)器需要根據(jù)索引掃描的結(jié)果回表,查詢所有符合條件的記錄皆愉。如果數(shù)據(jù)量巨大嗜价,這將產(chǎn)生大量隨機I/O,隨之幕庐,數(shù)據(jù)庫的平均響應(yīng)時間將大到不可接受的程度久锥。另外,索引維護(磁盤空間异剥、I/O操作)的代價也非常高瑟由。

為了解決單臺數(shù)據(jù)庫服務(wù)器的性能問題,提高系統(tǒng)的吞吐量時冤寿,就需要根據(jù)業(yè)務(wù)邏輯把表拆分成若干個歹苦,分別放在不同的數(shù)據(jù)庫服務(wù)器中以降低單臺DB的負載和緩解單庫IO的讀寫壓力青伤,降低訪問數(shù)據(jù)庫的平均響應(yīng)時間。

2.拆分方式

(1)垂直拆分

這里主要是指按業(yè)務(wù)平臺的不同類型功能模塊進行拆分殴瘦,比如分為訂單庫狠角、資源庫、用戶庫蚪腋。拆分之后丰歌,每個業(yè)務(wù)平臺中的應(yīng)用工程只訪問對應(yīng)的業(yè)務(wù)數(shù)據(jù)庫,一方面將單點數(shù)據(jù)庫拆分成了多個屉凯,分攤了單庫的讀寫IO壓力立帖;另一方面,拆分后的數(shù)據(jù)庫各自獨立悠砚,實現(xiàn)了業(yè)務(wù)隔離晓勇,不再互相影響值依。下圖為“垂直拆分”方式的結(jié)構(gòu)示意圖:

(2)水平拆分

按業(yè)務(wù)垂直切分后役首,可能有些單表數(shù)據(jù)還是很大,訪問性能低下辩棒,這時需要對這個單庫單表上的數(shù)據(jù)進行水平切分节榜。按照一定的分片算法(比如按照主鍵ID的Hash)將同一個表的數(shù)據(jù)進行切分并分別保存到不同數(shù)據(jù)庫的數(shù)據(jù)表中,且這些數(shù)據(jù)庫中的表結(jié)構(gòu)完全相同别智。以下為“水平拆分”示意圖:

在SOA/微服務(wù)架構(gòu)普遍流行的今天宗苍,從某種意義上來說,業(yè)務(wù)平臺的建設(shè)基本都是按照“垂直拆分”的思路來的薄榛,即不同業(yè)務(wù)子系統(tǒng)訪問對應(yīng)不同的業(yè)務(wù)數(shù)據(jù)庫讳窟。但是,“垂直拆分”的方案并沒有根本解決當某一業(yè)務(wù)微服務(wù)的數(shù)據(jù)量劇增后對單庫帶來的讀寫IO壓力敞恋。因此同一個業(yè)務(wù)平臺中丽啡,比如資源/性能數(shù)據(jù)庫,在“垂直拆分”后硬猫,我們一般會考慮“水平拆分”來應(yīng)對單庫單表不斷增加的場景补箍。

在這里講到的“水平拆分”其實跟上篇中提到的MySQL分區(qū)表有些類似,只是不同之處在于分區(qū)表是在單庫的情況下啸蜜,通過MYSQL存儲引擎來實現(xiàn)水平拆分坑雅,平臺系統(tǒng)本身的業(yè)務(wù)邏輯不需要感知這一改變。

“水平拆分”優(yōu)點:

a衬横、經(jīng)過拆分后裹粤,不存在單庫數(shù)據(jù)量大和高并發(fā)的性能瓶頸;

b蜂林、提高了系統(tǒng)的穩(wěn)定性和負載能力遥诉;

c拇泣、緩解單庫IO壓力;

“水平拆分”缺點:

a矮锈、分庫事務(wù)的一致性難以解決霉翔;

b、跨庫Join表性能問題愕难,邏輯復(fù)雜早龟;

c、跨庫count/order by/group by以及聚合函數(shù)問題猫缭;

d葱弟、切分策略如何選擇,策略問題很可能導(dǎo)致數(shù)據(jù)分布不均勻問題猜丹;

e芝加、全局主鍵問題;

(3)應(yīng)對水平拆分問題的方案

a射窒、跨庫事務(wù)問題:

解決跨庫事務(wù)問題主要可以通過兩種方法藏杖。第一種方法,使用前面篇幅介紹的分布式事務(wù)脉顿,簡單有效但是性能代價高蝌麸。第二種方式,應(yīng)用程序和數(shù)據(jù)庫共同控制保證艾疟,將一個跨多個數(shù)據(jù)庫的分布式事務(wù)分拆成多個僅處于單個數(shù)據(jù)庫上面的小事務(wù)来吩,并通過應(yīng)用程序來總控各個小事務(wù)。該方案優(yōu)點在于能夠靈活控制蔽莱,缺點在于改造量較大弟疆。

b、跨庫Join表的問題

對于業(yè)務(wù)平臺的數(shù)據(jù)持久層來說盗冷,涉及復(fù)雜的Join多表查詢在所難免怠苔。解決這一問題的普遍做法是分兩次查詢解決。在第一次查詢的結(jié)果集中找出關(guān)聯(lián)數(shù)據(jù)的id仪糖,根據(jù)這些id發(fā)起第二次請求得到關(guān)聯(lián)數(shù)據(jù)柑司。

c、跨節(jié)點的count,order by,group by以及聚合函數(shù)問題

與解決跨節(jié)點join問題的類似锅劝,只需要分別在各個單庫上得到結(jié)果后在業(yè)務(wù)應(yīng)用端進行合并帜羊。和join不同的是每個結(jié)點的查詢可以采用多線程方式并行執(zhí)行(在jdk8中可以用CompletableFuture解決),因此很多時候它的合并速度要比單個大數(shù)據(jù)量的表快很多鸠天。

d讼育、數(shù)據(jù)分片策略選擇

水平拆分中一個比較重要的問題就是按照什么邏輯策略來進行數(shù)據(jù)分片(即為拆分庫表)。一種方案是按照地域類的屬性進行拆分;另外一種方案則是按照訂單ID/用戶ID進行拆分奶段。按照地域類的屬性進行拆分會使得數(shù)據(jù)聚合度比較高饥瓷,做聚合查詢比較簡單,實現(xiàn)也相對簡單痹籍,缺點是數(shù)據(jù)分布不均勻呢铆。按訂單ID拆分則正相反,優(yōu)點是數(shù)據(jù)分布均勻蹲缠,不會出現(xiàn)一個數(shù)據(jù)庫數(shù)據(jù)極大或極小的情況棺克,缺點是數(shù)據(jù)太分散,不利于做聚合查詢线定。比如娜谊,按訂單ID拆分后,一個客戶的訂單可能分布在不同的數(shù)據(jù)庫中斤讥,查詢一個客戶下面的所有訂單纱皆,可能需要查詢多個數(shù)據(jù)庫。對于這種情況芭商,一種解決方案是將需要聚合查詢的數(shù)據(jù)做冗余表派草,冗余的表不做拆分,同時在業(yè)務(wù)開發(fā)過程中铛楣,減少聚合查詢近迁。

e、全局主鍵問題

原本依賴數(shù)據(jù)庫生成主鍵(比如自增)的表在拆分后需要自己實現(xiàn)主鍵的生成簸州,因為一般拆分規(guī)則是建立在主鍵上的钳踊,所以在插入新數(shù)據(jù)時需要確定主鍵后才能找到存儲的表。在實際應(yīng)用中勿侯,可以參考flickr的全局主鍵生成方案和uuid的全局主鍵生成方案。

3.拆分庫表解決方案

那么問題來了:采用分庫分表技術(shù)方案的話缴罗,用開源方案還是自研助琐?

一般在業(yè)務(wù)平臺建設(shè)前期綜合評估系統(tǒng)容量、性能面氓、吞吐量等因素后兵钮,會考慮采用分庫分表的解決方案,那么就會遇到以上的問題舌界。如果自主研發(fā)的話掘譬,時間周期長、成本高呻拌、項目風險大葱轩,因此都會考慮采用目前業(yè)界比較成熟的開源分庫分表方案。下面主要闡述幾種開源的分庫分表解決方案:

(1)Sharding-JDBC

Sharding-JDBC是當當網(wǎng)開源的分布式數(shù)據(jù)庫中間件,其代表了客戶端類的分庫分表框架靴拱。它定位為輕量級java框架垃喊,由客戶端直連數(shù)據(jù)庫,以jar包形式提供服務(wù)袜炕,未使用中間層本谜,無需額外部署,無其他依賴偎窘,數(shù)據(jù)庫運維人員無需改變原有的運維方式乌助,即為增強版的JDBC驅(qū)動,舊代碼遷移成本幾乎為零陌知。同時他托,Sharding-JDBC完整的實現(xiàn)了分庫分表,讀寫分離和分布式主鍵功能纵诞,并初步實現(xiàn)了柔性事務(wù)上祈。下面是Sharding-JDBC的框架圖:

從上面的框架圖中可以看出Sharding-JDBC這一款分庫分表的中間件分為分片規(guī)則配置、SQL解析浙芙、SQL改寫登刺、SQL路由、SQL執(zhí)行以及結(jié)果歸并等模塊嗡呼。

a.分片規(guī)則配置

Sharding-JDBC的分片邏輯非常靈活纸俭,支持分片策略自定義、復(fù)數(shù)分片鍵南窗、多運算符分片等功能揍很。舉個例子:根據(jù)用戶ID分庫/訂單ID分表這種分庫分表結(jié)合的分片策略;或根據(jù)年分庫万伤,月份+用戶區(qū)域ID分表這樣的多片鍵分片窒悔。Sharding-JDBC除了支持等號運算符進行分片,還支持in/between運算符分片敌买,提供了更加強大的分片功能简珠。Sharding-JDBC提供了spring命名空間用于簡化配置,以及規(guī)則引擎用于簡化策略編寫虹钮。

b.JDBC規(guī)范重寫

Sharding-JDBC中間件對標準JDBC規(guī)范的重寫思路是針對DataSource聋庵、Connection、Statement芙粱、PreparedStatement和ResultSet五個核心接口封裝祭玉,將多個JDBC實現(xiàn)類集合(如:MySQL JDBC實現(xiàn)/DBCP JDBC實現(xiàn)等)納入Sharding-JDBC實現(xiàn)類管理。

c.SQL解析

SQL解析作為分庫分表類中間件框架的核心之一春畔,其性能和兼容性是最重要的衡量指標脱货。目前常見的SQL解析器主要有fdb/jsqlparser和Druid岛都。Sharding-JDBC采用解析速度最快的Druid作為SQL解析器。

d.SQL改寫

這里一部分是將分表的邏輯表名稱替換為真實表名稱蹭劈。另一部分是根據(jù)SQL解析結(jié)果替換一些在分片環(huán)境中不正確的功能疗绣。

e. SQL路由

SQL路由是指根據(jù)分片規(guī)則配置,將待執(zhí)行的SQL定位至真正的DB數(shù)據(jù)源铺韧。

f. SQL執(zhí)行

這里指的是路由至真實的DB數(shù)據(jù)源后多矮,Sharding-JDBC將采用多線程并發(fā)執(zhí)行SQL,并完成對addBatch等批量方法的處理哈打。

g.結(jié)果歸并

Sharding-JDBC支持通遍歷類塔逃、排序類、聚合類和分組類四種結(jié)果并歸方式料仗。普通遍歷類最為簡單湾盗,只需按順序遍歷ResultSet的集合即可。排序類結(jié)果則將結(jié)果先排序再輸出立轧,因為各分庫的分片結(jié)果均按照各自條件完成排序格粪,所以采用歸并排序算法整合最終結(jié)果。聚合類分為3種類型氛改,比較型帐萎、累加型和平均值型。分組類相對最為復(fù)雜胜卤,需要將所有的ResultSet結(jié)果放入內(nèi)存疆导,使用Map-Reduce算法分組,最后根據(jù)排序和聚合條件做相關(guān)處理葛躏。最為消耗內(nèi)存澈段,損失性能的地方就是這里了,可以考慮使用limit合理的限制分組數(shù)據(jù)大小舰攒。

鑒于以上講的幾個Sharding-JDBC中間件框架特點败富,該方案的優(yōu)點在于,業(yè)務(wù)平臺中的服務(wù)應(yīng)用可以直接連數(shù)據(jù)庫摩窃,降低外圍系統(tǒng)依賴所帶來的不穩(wěn)定風險兽叮,系統(tǒng)集成難度較低,無需額外運維組件偶芍。

其框架的缺點是,限制服務(wù)應(yīng)用只能在業(yè)務(wù)邏輯層中進行定制化的開發(fā)和優(yōu)化德玫,擴展性一般匪蟀。對于,比較復(fù)雜的系統(tǒng)可能會力不從心宰僧,將分片邏輯的壓力放在應(yīng)用服務(wù)器上材彪,造成額外風險。(由于本文篇幅所限,對于Sharding-JDBC中間件開源代碼的深度分析將在后續(xù)篇幅中會講到)

(2)MyCat

MyCat是一個開源的分布式數(shù)據(jù)庫系統(tǒng)(屬于代理類框架)段化,是一個實現(xiàn)MySQL協(xié)議的服務(wù)器嘁捷,用戶可以把它看作是一個數(shù)據(jù)庫代理,用MySQL客戶端工具和命令行訪問显熏,而其后端可以用MySQL原生協(xié)議與多個MySQL服務(wù)器通信雄嚣,也可以用JDBC協(xié)議與大多數(shù)主流數(shù)據(jù)庫服務(wù)器通信,其核心功能是分表分庫喘蟆,即將一個大表水平分割為N個小表缓升,存儲在后端MySQL服務(wù)器里或者其他數(shù)據(jù)庫里。其框架如下:

MyCat分庫分表方案的優(yōu)點在于蕴轨,能夠處理非常復(fù)雜的需求港谊,不受數(shù)據(jù)庫訪問層原來實現(xiàn)的限制,擴展性強且對于應(yīng)用服務(wù)透明且沒有增加任何額外負載橙弱。其缺點是上線部署需要額外的中間件歧寺,增加運維成本;應(yīng)用服務(wù)需經(jīng)過代理來連接數(shù)據(jù)庫棘脐,網(wǎng)絡(luò)上多了一層鏈接的開銷斜筐,性能有損失且有額外風險。

本文從幾個不同的應(yīng)用開發(fā)視角荆残,分別闡述了作者自己工作中用到過的業(yè)務(wù)平臺數(shù)據(jù)庫架構(gòu)優(yōu)化方案奴艾,包括分離熱點數(shù)據(jù)、本地/分布式緩存内斯、分庫分表的三種技術(shù)架構(gòu)蕴潦。限于作者的才疏學淺,可能對幾種方案的理解不夠深入俘闯,如有闡述不合理之處還望留言一起探討潭苞。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市真朗,隨后出現(xiàn)的幾起案子此疹,更是在濱河造成了極大的恐慌,老刑警劉巖遮婶,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蝗碎,死亡現(xiàn)場離奇詭異,居然都是意外死亡旗扑,警方通過查閱死者的電腦和手機蹦骑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來臀防,“玉大人眠菇,你說我怎么就攤上這事边败。” “怎么了捎废?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵笑窜,是天一觀的道長。 經(jīng)常有香客問我登疗,道長排截,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任谜叹,我火速辦了婚禮匾寝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘荷腊。我一直安慰自己艳悔,他們只是感情好,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布女仰。 她就那樣靜靜地躺著猜年,像睡著了一般。 火紅的嫁衣襯著肌膚如雪疾忍。 梳的紋絲不亂的頭發(fā)上乔外,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天,我揣著相機與錄音一罩,去河邊找鬼杨幼。 笑死,一個胖子當著我的面吹牛聂渊,可吹牛的內(nèi)容都是我干的差购。 我是一名探鬼主播,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼汉嗽,長吁一口氣:“原來是場噩夢啊……” “哼欲逃!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起饼暑,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤稳析,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后弓叛,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體彰居,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年撰筷,在試婚紗的時候發(fā)現(xiàn)自己被綠了陈惰。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡闭专,死狀恐怖奴潘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情影钉,我是刑警寧澤画髓,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站平委,受9級特大地震影響奈虾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜廉赔,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一肉微、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蜡塌,春花似錦碉纳、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至琅摩,卻和暖如春铁孵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背房资。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工蜕劝, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人轰异。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓岖沛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親溉浙。 傳聞我的和親對象是個殘疾皇子烫止,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容