《高性能MySQL》讀后感——聚簇索引

聚簇索引并不是一種單獨(dú)的索引類型,而是一種數(shù)據(jù)存儲方式。比如,InnoDB的聚簇索引使用B+Tree的數(shù)據(jù)結(jié)構(gòu)存儲索引和數(shù)據(jù)。聚簇索引與非聚簇對比如下圖。


圖0 聚簇和非聚簇表形象對比圖

當(dāng)表有聚簇索引時(shí),它的數(shù)據(jù)行實(shí)際上存放在索引的葉子頁(leaf page)中。因?yàn)闊o法同時(shí)把數(shù)據(jù)行存放在兩個(gè)不同的地方,所以一個(gè)表只能有一個(gè)聚簇索引(不過,覆蓋索引可以模擬多個(gè)聚簇索引的情況)。

  • 術(shù)語“聚簇”表示數(shù)據(jù)行和相鄰的鍵值緊湊地存儲在一起。
  • 聚簇索引的二級索引:葉子節(jié)點(diǎn)不會保存引用的行的物理位置,而是保存行的主鍵值。

對于聚簇索引的存儲引擎,數(shù)據(jù)的物理存放順序與索引順序是一致的,即:只要索引是相鄰的,那么對應(yīng)的數(shù)據(jù)一定也是相鄰地存放在磁盤上的,如果主鍵不是自增id,可以想象,它會干些什么,不斷地調(diào)整數(shù)據(jù)的物理地址、分頁,當(dāng)然也有其他一些措施來減少這些操作,但卻無法徹底避免。但,如果是自增的,那就簡單了,它只需要一頁一頁地寫,索引結(jié)構(gòu)相對緊湊,磁盤碎片少,效率也高。

對于非聚簇索引的存儲引擎,表數(shù)據(jù)存儲順序與索引順序無關(guān),葉結(jié)點(diǎn)包含索引字段值及指向數(shù)據(jù)頁數(shù)據(jù)行的邏輯指針,其行數(shù)量與數(shù)據(jù)表行數(shù)據(jù)量一致。

下圖1展示了聚簇索引的記錄是如何存放的。注意到,節(jié)點(diǎn)頁只包含了索引列,葉子頁包含行的全部數(shù)據(jù),這是B+Tree的數(shù)據(jù)結(jié)構(gòu)。在這個(gè)案例中,索引列包含的是整數(shù)值。

圖1 聚簇索引的數(shù)據(jù)分布

InnoDB將通過主鍵聚集數(shù)據(jù),圖1中的“被索引的列”就是主鍵列。如果沒有定義主鍵,InnoDB會選擇一個(gè)唯一的非空索引代替。如果沒有這樣的索引,InnoDB會隱式定義一個(gè)主鍵來作為聚簇索引。InnoDB只聚集在同一個(gè)頁面中的記錄,包含相鄰鍵值的頁面可能會相距甚遠(yuǎn)。

聚簇主鍵可能對性能有幫助,但也可能導(dǎo)致嚴(yán)重的性能問題。所以需要仔細(xì)地考慮聚簇索引,尤其是將表的存儲引擎從InnoDB改成其他引擎的時(shí)候(反過來也一樣)。

聚簇的數(shù)據(jù)有一些重要的優(yōu)點(diǎn):

  • 可以把相關(guān)數(shù)據(jù)保存在一起。例如實(shí)現(xiàn)電子郵箱時(shí),可以根據(jù)用戶ID來聚集數(shù)據(jù),這樣只需要從磁盤讀取少數(shù)的數(shù)據(jù)頁就能獲取某個(gè)用戶的全部郵件。如果沒有聚簇索引,則每封郵件都可能多一次磁盤IO。
  • 數(shù)據(jù)訪問更快。聚簇索引將索引和數(shù)據(jù)保存在同一個(gè)B+Tree中,因此從聚簇索引中獲取數(shù)據(jù)通常比在非聚簇索引中查找要快。
  • 使用覆蓋索引掃描的查詢可以直接使用頁節(jié)點(diǎn)中的主鍵值。

如果設(shè)計(jì)表和查詢時(shí)能充分利用上面的優(yōu)點(diǎn),就能極大地提升性能。但是,聚簇索引也有一些缺點(diǎn):

  • 聚簇?cái)?shù)據(jù)最大限度地提高了IO密集型應(yīng)用的性能,但如果數(shù)據(jù)全部放在內(nèi)存中,則訪問的順序就沒那么重要了,聚簇索引也就沒什么優(yōu)勢了。
  • 插入速度嚴(yán)重依賴于插入順序。按照主要的順序插入是加載數(shù)據(jù)到InnoDB表中速度最快的方式。但如果不是按照主鍵順序加載數(shù)據(jù),那么在加載完成后最好使用optimize table命令重新組織一下表。
  • 更新聚簇索引列的代價(jià)很高,因?yàn)闀?qiáng)制InnoDB將每個(gè)被更新的行移動到新的位置。
  • 基于聚簇索引的表插入新行,或者主鍵被更新導(dǎo)致需要移動行的時(shí)候,可能面臨”頁分裂(page split)“的問題。當(dāng)行的主鍵值要求必須將這一行插入到某個(gè)已滿的頁中時(shí),存儲引擎會將該頁分裂成兩個(gè)頁面來容納該行,這就是一次分裂操作。頁分裂會導(dǎo)致表占用更多的磁盤空間。
  • 聚簇索引可能導(dǎo)致全表掃描變慢,尤其是行比較稀疏,或者由于頁分裂導(dǎo)致數(shù)據(jù)存儲不連續(xù)的時(shí)候。
  • 二級索引(非聚簇索引)可能比想象的要更大,因?yàn)樵诙o索引的葉子節(jié)點(diǎn)包含了引用行的主鍵列。
  • 二級索引訪問需要兩次索引查找,而不是一次。

最后一點(diǎn)可能讓人有些疑惑,為什么二級索引需要兩次索引查找?答案在于二級索引中保存的”行指針“的實(shí)質(zhì)。要記住,二級索引葉子節(jié)點(diǎn)保存的不是指向行的物理位置的指針,而是行的主鍵值。

這意味著通過二級索引查找行,存儲引擎需要找到二級索引的葉子節(jié)點(diǎn)獲得對應(yīng)的主鍵值,然后根據(jù)這個(gè)值去聚簇索引中查找到對應(yīng)的行。這里做了重復(fù)的工作:兩次B-Tree查找而不是一次。對于 InnoDB,自適應(yīng)哈希索引能夠減少這樣的重復(fù)工作。

InnoDB和MyISAM的數(shù)據(jù)分布對比

聚簇索引和非聚簇索引的數(shù)據(jù)分布有區(qū)別,以及對應(yīng)的主要索引和二級索引的數(shù)據(jù)分布也有區(qū)別,通常會讓人感到困擾和意外。來看看InnoDB和MyISAM是如何存儲下面這個(gè)表的:

create table layout_test(
    col1 int not null,
    col2 int not null,
    primary key(col1),
    key(col2)
);

假設(shè)該表的主鍵取值為1~10000,按照隨機(jī)順序播放并使用optimize table命令做了優(yōu)化。換句話說,數(shù)據(jù)在磁盤上的存儲方式已經(jīng)最優(yōu),但行的順序是隨機(jī)的。列col2的值是從1~100之間隨機(jī)賦值,所以有很多重復(fù)的值。

MyISAM的數(shù)據(jù)布局

MyISAM的B+Tree的葉子節(jié)點(diǎn)上的data,并不是數(shù)據(jù)本身,而是數(shù)據(jù)存放的地址。MyISAM按照數(shù)據(jù)插入的順序存儲在磁盤上,如下圖2所示,左邊為行號(row number),從0開始。因?yàn)樵M的大小固定,所以MyISAM很容易的從表的開始位置找到某一字節(jié)的位置。

圖2 MyISAM表layout_test的數(shù)據(jù)分布

MyISAM建立的primary key的索引結(jié)構(gòu)大致如圖3和圖4所示。MyISAM不支持聚簇索引,索引中每一個(gè)葉子節(jié)點(diǎn)僅僅包含行號(row number),且葉子節(jié)點(diǎn)按照col1的順序存儲。MyISAM是按列值與行號來組織索引的。

圖3 MyISAM表layout_test的主鍵分布

在圖4中,表一共有三列,假設(shè)以Col1為主鍵,可以看出,MyISAM的葉子節(jié)點(diǎn)中保存的實(shí)際上是指向存放數(shù)據(jù)的物理塊的指針。從MYISAM存儲的物理文件看出,MyISAM引擎的索引文件(.MYI)和數(shù)據(jù)文件(.MYD)是相互獨(dú)立的,索引文件僅僅保存數(shù)據(jù)記錄的地址。

圖4 MyISAM主鍵索引的分布

下圖5顯示col2 的索引結(jié)構(gòu),與圖3的primary key對比,索引中每一個(gè)葉子節(jié)點(diǎn)僅僅包含行號(row number),且葉子節(jié)點(diǎn)按照col2的順序存儲。在圖6中,在Col2建立一個(gè)輔助索引,與圖4對比,MyISAM的葉子節(jié)點(diǎn)也是保存指向存放數(shù)據(jù)的物理塊的指針。

所以,結(jié)論是MyISAM的primary key和輔助索引沒有任何區(qū)別。只是Primary key要求key唯一非空,而輔助索引的key可以重復(fù)。

圖5 MyISAM表layout_test的col2列索引的分布
圖6 MyISAM輔助索引的分布

因此,MyISAM中索引檢索的算法為首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,則取出其data域的值,然后以data域的值為地址,讀取相應(yīng)數(shù)據(jù)記錄。

InnoDB的數(shù)據(jù)布局

MyISAM索引文件和數(shù)據(jù)文件是分離的,索引文件僅保存數(shù)據(jù)記錄的地址。而在InnoDB中,表數(shù)據(jù)文件本身就是按B+Tree組織的一個(gè)索引結(jié)構(gòu),這棵樹的葉節(jié)點(diǎn)data域保存了完整的數(shù)據(jù)記錄。這個(gè)索引的key是數(shù)據(jù)表的主鍵,因此InnoDB表數(shù)據(jù)文件本身就是主索引。

圖7和與圖3 MyISAM對比看出,InnoDB索引的每一個(gè)葉子節(jié)點(diǎn)都包含了主鍵值、事務(wù)ID、用于事務(wù)和MVCC的回流指針以及所有的剩余列(在這個(gè)例子中是col2)。如果主鍵是一個(gè)列前綴索引,InnoDB也會包含完整的主鍵列和剩下的其他列。這種索引叫做聚簇索引

圖8可以看到葉節(jié)點(diǎn)包含了完整的數(shù)據(jù)記錄。

因?yàn)镮nnoDB的數(shù)據(jù)文件本身要按主鍵聚集,所以InnoDB要求表必須有主鍵(MyISAM可以沒有),如果沒有顯式指定,則MySQL系統(tǒng)會自動選擇一個(gè)可以唯一標(biāo)識數(shù)據(jù)記錄的列作為主鍵,如果不存在這種列,則MySQL自動為InnoDB表生成一個(gè)隱含字段作為主鍵,這個(gè)字段長度為6個(gè)字節(jié),類型為長整形。

圖7 InnoDB表layout_test的主鍵分布
圖8 InnoDB主鍵索引的分布

還有一點(diǎn)和MyISAM的不同是,InnoDB的二級索引和聚簇索引很不相同。InnoDB二級索引的葉子節(jié)點(diǎn)中存儲的不是”行指針“,而是主鍵值,并以此作為指向行的“指針”。這樣的策略減少了當(dāng)出現(xiàn)行移動或者數(shù)據(jù)頁分裂時(shí)二級索引的維護(hù)工作。使用主鍵值當(dāng)作指針會讓二級索引占用更多的空間,換來的好處是,InnoDB在移動行時(shí)無須更新二級索引中的這個(gè)“指針”。

下圖9展示了示例表的二級索引col2索引。每一個(gè)葉子節(jié)點(diǎn)都包含了索引列(這里是col2),緊接著是主鍵值(col1)。圖10展示了InnoDB的所有輔助索引都引用主鍵作為data域。

圖9 InnoDB表layout_test的col2列索引的分布
圖10 InnoDB輔助索引的分布

InnoDB 表是基于聚簇索引建立的。因此InnoDB 的索引能提供一種非常快速的主鍵查找性能。不過,它的輔助索引(Secondary Index, 也就是非主鍵索引)也會包含主鍵列,所以,如果主鍵定義的比較大,其他索引也將很大。如果想在表上定義 、很多索引,則爭取盡量把主鍵定義得小一些。InnoDB 不會壓縮索引。

InnoDB與MyIASM索引和數(shù)據(jù)布局對比

圖7描述InnoDB和MyISAM如何存放表的抽象圖。對比InnoDB和MyISAM的主鍵索引與二級索引。

InnoDB的的二級索引的葉子節(jié)點(diǎn)存放的是KEY字段加主鍵值。因此,通過二級索引查詢首先查到是主鍵值,然后InnoDB再根據(jù)查到的主鍵值通過主鍵索引找到相應(yīng)的數(shù)據(jù)塊。而MyISAM的二級索引葉子節(jié)點(diǎn)存放的還是列值與行號的組合,葉子節(jié)點(diǎn)中保存的是數(shù)據(jù)的物理地址。所以可以看出MYISAM的主鍵索引和二級索引沒有任何區(qū)別,主鍵索引僅僅只是一個(gè)叫做PRIMARY的唯一、非空的索引,且MYISAM引擎中可以不設(shè)主鍵。

圖7 聚簇和非聚簇表對比圖

為了更形象說明這兩種索引的區(qū)別,我們假想一個(gè)表如下圖8存儲了4行數(shù)據(jù)。其中id作為主索引,name作為輔助索引。圖示清晰的顯示了聚簇索引和非聚簇索引的差異。

對于聚簇索引存儲來說,行數(shù)據(jù)和主鍵B+樹存儲在一起,輔助鍵B+樹只存儲輔助鍵和主鍵,主鍵和非主鍵B+樹幾乎是兩種類型的樹。對于非聚簇索引存儲來說,主鍵B+樹在葉子節(jié)點(diǎn)存儲指向真正數(shù)據(jù)行的指針,而非主鍵。

InnoDB使用的是聚簇索引,將主鍵組織到一棵B+樹中,而行數(shù)據(jù)就儲存在葉子節(jié)點(diǎn)上,若使用"where id = 14"這樣的條件查找主鍵,則按照B+樹的檢索算法即可查找到對應(yīng)的葉節(jié)點(diǎn),之后獲得行數(shù)據(jù)。若對Name列進(jìn)行條件搜索,則需要兩個(gè)步驟:第一步在輔助索引B+樹中檢索Name,到達(dá)其葉子節(jié)點(diǎn)獲取對應(yīng)的主鍵。第二步使用主鍵在主索引B+樹種再執(zhí)行一次B+樹檢索操作,最終到達(dá)葉子節(jié)點(diǎn)即可獲取整行數(shù)據(jù)。

MyISM使用的是非聚簇索引,非聚簇索引的兩棵B+樹看上去沒什么不同,節(jié)點(diǎn)的結(jié)構(gòu)完全一致只是存儲的內(nèi)容不同而已,主鍵索引B+樹的節(jié)點(diǎn)存儲了主鍵,輔助鍵索引B+樹存儲了輔助鍵。表數(shù)據(jù)存儲在獨(dú)立的地方,這兩顆B+樹的葉子節(jié)點(diǎn)都使用一個(gè)地址指向真正的表數(shù)據(jù),對于表數(shù)據(jù)來說,這兩個(gè)鍵沒有任何差別。由于索引樹是獨(dú)立的,通過輔助鍵檢索無需訪問主鍵的索引樹。

圖8 聚簇和非聚簇表形象對比圖

我們重點(diǎn)關(guān)注聚簇索引,看上去聚簇索引的效率明顯要低于非聚簇索引,因?yàn)槊看问褂幂o助索引檢索都要經(jīng)過兩次B+樹查找,這不是多此一舉嗎?聚簇索引的優(yōu)勢在哪?
1 由于行數(shù)據(jù)和葉子節(jié)點(diǎn)存儲在一起,這樣主鍵和行數(shù)據(jù)是一起被載入內(nèi)存的,找到葉子節(jié)點(diǎn)就可以立刻將行數(shù)據(jù)返回了,如果按照主鍵Id來組織數(shù)據(jù),獲得數(shù)據(jù)更快。
2 輔助索引使用主鍵作為"指針" 而不是使用地址值作為指針的好處是,減少了當(dāng)出現(xiàn)行移動或者數(shù)據(jù)頁分裂時(shí)輔助索引的維護(hù)工作,使用主鍵值當(dāng)作指針會讓輔助索引占用更多的空間,換來的好處是InnoDB在移動行時(shí)無須更新輔助索引中的這個(gè)"指針"。也就是說行的位置(實(shí)現(xiàn)中通過16K的Page來定位,后面會涉及)會隨著數(shù)據(jù)庫里數(shù)據(jù)的修改而發(fā)生變化(前面的B+樹節(jié)點(diǎn)分裂以及Page的分裂),使用聚簇索引就可以保證不管這個(gè)主鍵B+樹的節(jié)點(diǎn)如何變化,輔助索引樹都不受影響。

在InnoDB表中按主鍵順序插入行

如果正在使用InnoDB表并且沒有什么數(shù)據(jù)需要聚集,那么可以定義一個(gè)代理鍵作為主鍵,這種主鍵的數(shù)據(jù)應(yīng)該和應(yīng)用無關(guān),最簡單的方法是使用auto_increment自增列。這樣可以保證數(shù)據(jù)行是按照順序?qū)懭耄瑢τ诟鶕?jù)主鍵做關(guān)聯(lián)操作的性能也會更好。

最好避免隨機(jī)的聚簇索引,特別對于I/O密集型的應(yīng)用。例如,從性能的角度考慮,使用UUID作為聚簇索引會很糟糕:它使得聚簇索引的插入變得完全隨機(jī),這是最壞的情況,使得數(shù)據(jù)沒有任何聚集特性。

為了演示這一點(diǎn),我們做如下兩個(gè)基準(zhǔn)測試。第一個(gè)使用整數(shù)ID插入shopinfo表,整數(shù)ID自增且為主鍵:

CREATE TABLE `shopinfo` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '記錄ID',
  `shop_id` int(11) NOT NULL COMMENT '商店ID',
  `goods_id` int(11) NOT NULL COMMENT '物品ID',
  `pay_type` int(11) NOT NULL COMMENT '支付方式',
  `price` decimal(10,2) NOT NULL COMMENT '物品價(jià)格',
  `comment` varchar(4000) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `shop_id` (`shop_id`,`goods_id`),
  KEY `price` (`price`),
  KEY `pay_type` (`pay_type`),
  KEY `idx_comment` (`comment`(255))
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商店物品表';

第二個(gè)例子是shopinfo_uuid表,除了主鍵改為UUID,其余和前面的shopinfo表完全相同。

CREATE TABLE `shopinfo_uuid` (
  `uuid` varchar(36) NOT NULL,
  `shop_id` int(11) NOT NULL COMMENT '商店ID',
  `goods_id` int(11) NOT NULL COMMENT '物品ID',
  `pay_type` int(11) NOT NULL COMMENT '支付方式',
  `price` decimal(10,2) NOT NULL COMMENT '物品價(jià)格',
  `comment` varchar(4000) DEFAULT NULL,
  PRIMARY KEY (`uuid`),
  UNIQUE KEY `shop_id` (`shop_id`,`goods_id`),
  KEY `price` (`price`),
  KEY `pay_type` (`pay_type`),
  KEY `idx_comment` (`comment`(255))
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商店物品表';

我們先向這兩個(gè)表各插入1萬條記錄。然后再向這兩個(gè)表繼續(xù)插入9萬條記錄,觀察這兩個(gè)表的插入耗時(shí)和表索引大小,下表對測試結(jié)果進(jìn)行比較。其中,查看指定庫的指定表shopinfo的索引大小SQL語句:
SELECT CONCAT(ROUND(SUM(index_length)/(1024*1024), 2), ' MB') AS 'Total Index Size' FROM TABLES WHERE table_schema = 'study' and table_name = 'shopinfo';

表名 行數(shù) 時(shí)間 索引大小(MB)
shopinfo 10000 0.755s 4.08
shopinfo_uuid 10000 1.699s 8.16
shopinfo 90000 8.014s 29.47
shopinfo_uuid 90000 46.111s 60.58

通過測試,插入同樣的行數(shù)和內(nèi)容(除主鍵內(nèi)容),向UUID主鍵插入行不僅花費(fèi)的時(shí)間更長,而且索引占用的空間也更大。這一方面是由于主鍵字段更長,另一方面毫無疑問是由于頁分裂和碎片導(dǎo)致的。

如圖9所示,由于主鍵的值是順序的,InnoDB把每一條記錄都存儲在上一條記錄的后面。當(dāng)達(dá)到頁的最大填充因子時(shí)(InnoDB默認(rèn)的最大填充因子是頁大小的15/16,留出的部分空間用于以后修改),下一條記錄就會寫入新的頁中。一旦數(shù)據(jù)按照這樣順序的方式加載,主鍵頁就會近似于被順序的記錄填滿,這也是所期望的結(jié)果。

圖9 向聚簇索引插入順序的索引值

而當(dāng)采用UUID的聚簇索引的表往插入數(shù)據(jù),如圖10所示,因?yàn)樾滦械闹麈I值不一定比之前的插入值大,所以InnoDB無法簡單的總是把新行插入到索引的最后,而是需要為新的行尋找合適的位置----通常是已有數(shù)據(jù)的中間位置----并且分配空間。這會增加很多額外的工作,并導(dǎo)致數(shù)據(jù)分布不夠優(yōu)化。

圖10 向聚簇索引插入無序的值

下面總結(jié)使用UUID作為主鍵的一些缺點(diǎn):

  • 寫入目標(biāo)頁可能已經(jīng)刷到磁盤上并從緩存中移除,或者是還沒有被加載到緩存中,InnoDB在插入之前不得不先找到并從磁盤讀取目標(biāo)頁到內(nèi)存中,這將導(dǎo)致大量的隨機(jī)I/O;
  • 因?yàn)閷懭胧莵y序的,InnoDB不得不頻繁的做頁分裂操作,以便為新的行分配空間。頁分裂會導(dǎo)致移動大量數(shù)據(jù),一次插入最少需要修改三個(gè)頁而不是一個(gè),包含兩個(gè)葉子節(jié)點(diǎn)和一個(gè)父節(jié)點(diǎn)。
  • 由于頻繁的頁分裂,頁會變得稀疏并被不規(guī)則的填充,所以最終數(shù)據(jù)會有碎片。

把這些隨機(jī)值載入到聚簇索引以后,需要做一次optimize table來重建表并優(yōu)化頁的填充。

注意,順序主鍵也有缺點(diǎn):對于高并發(fā)工作負(fù)載,在InnoDB中按主鍵順序插入可能會造成明顯的爭用。主鍵的上界會成為“熱點(diǎn)”。因?yàn)樗械牟迦攵及l(fā)生在這里,所以并發(fā)插入可能導(dǎo)致間隙鎖競爭。另一個(gè)熱點(diǎn)可能是auto_increment鎖機(jī)制;如果遇到這個(gè)問題,則可能需要考慮重新設(shè)計(jì)表或者應(yīng)用,比如應(yīng)用層面生成單調(diào)遞增的主鍵ID,插表不使用auto_increment機(jī)制,或者更改innodb_autonc_lock_mode配置。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,428評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,024評論 3 413
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,285評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,548評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,328評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,878評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,971評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,098評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,616評論 1 331
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,554評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,725評論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,243評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,971評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,361評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,613評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,339評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,695評論 2 370

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