分布式鎖解決并發(fā)的三種實(shí)現(xiàn)方式

轉(zhuǎn)載自(Tank丶Farmer)

在很多場(chǎng)景中快集,我們?yōu)榱吮WC數(shù)據(jù)的最終一致性贡羔,需要很多的技術(shù)方案來支持,比如分布式事務(wù)个初、分布式鎖等乖寒。有的時(shí)候,我們需要保證一個(gè)方法在同

一時(shí)間內(nèi)只能被同一個(gè)線程執(zhí)行院溺。在單機(jī)環(huán)境中楣嘁,Java中其實(shí)提供了很多并發(fā)處理相關(guān)的API,但是這些API在分布式場(chǎng)景中就無能為力了珍逸。也就是說單

純的Java Api并不能提供分布式鎖的能力逐虚。所以針對(duì)分布式鎖的實(shí)現(xiàn)目前有多種方案:

分布式鎖一般有三種實(shí)現(xiàn)方式:1. 數(shù)據(jù)庫鎖;2. 基于Redis的分布式鎖谆膳;3. 基于ZooKeeper的分布式鎖叭爱。

分布式鎖應(yīng)該是怎么樣的

互斥性 可以保證在分布式部署的應(yīng)用集群中,同一個(gè)方法在同一時(shí)間只能被一臺(tái)機(jī)器上的一個(gè)線程執(zhí)行漱病。

這把鎖要是一把可重入鎖(避免死鎖)

不會(huì)發(fā)生死鎖:有一個(gè)客戶端在持有鎖的過程中崩潰而沒有解鎖买雾,也能保證其他客戶端能夠加鎖

這把鎖最好是一把阻塞鎖(根據(jù)業(yè)務(wù)需求考慮要不要這條)

有高可用的獲取鎖和釋放鎖功能

獲取鎖和釋放鎖的性能要好

數(shù)據(jù)庫鎖

基于數(shù)據(jù)庫表

要實(shí)現(xiàn)分布式鎖把曼,最簡(jiǎn)單的方式可能就是直接創(chuàng)建一張鎖表,然后通過操作該表中的數(shù)據(jù)來實(shí)現(xiàn)了漓穿。

當(dāng)我們要鎖住某個(gè)方法或資源時(shí)嗤军,我們就在該表中增加一條記錄,想要釋放鎖的時(shí)候就刪除這條記錄器净。

CREATE TABLE `methodLock` (

? `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵',

? `method_name` varchar(64) NOT NULL DEFAULT '' COMMENT '鎖定的方法名',

? `desc` varchar(1024) NOT NULL DEFAULT '備注信息',

? `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '保存數(shù)據(jù)時(shí)間型雳,自動(dòng)生成',

? PRIMARY KEY (`id`),

? UNIQUE KEY `uidx_method_name` (`method_name `) USING BTREE

) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='鎖定中的方法';

當(dāng)我們想要鎖住某個(gè)方法時(shí)当凡,執(zhí)行以下SQL:

insert into methodLock(method_name,desc) values (‘method_name’,‘desc’)

因?yàn)槲覀儗?duì)method_name做了唯一性約束山害,這里如果有多個(gè)請(qǐng)求同時(shí)提交到數(shù)據(jù)庫的話,數(shù)據(jù)庫會(huì)保證只有一個(gè)操作可以成功沿量,那么我們就可以認(rèn)為

操作成功的那個(gè)線程獲得了該方法的鎖浪慌,可以執(zhí)行方法體內(nèi)容。

當(dāng)方法執(zhí)行完畢之后朴则,想要釋放鎖的話权纤,需要執(zhí)行以下Sql:

delete from methodLock where method_name ='method_name'

上面這種簡(jiǎn)單的實(shí)現(xiàn)有以下幾個(gè)問題:

1、這把鎖強(qiáng)依賴數(shù)據(jù)庫的可用性乌妒,數(shù)據(jù)庫是一個(gè)單點(diǎn)汹想,一旦數(shù)據(jù)庫掛掉,會(huì)導(dǎo)致業(yè)務(wù)系統(tǒng)不可用撤蚊。

2古掏、這把鎖沒有失效時(shí)間,一旦解鎖操作失敗侦啸,就會(huì)導(dǎo)致鎖記錄一直在數(shù)據(jù)庫中槽唾,其他線程無法再獲得到鎖。

3光涂、這把鎖只能是非阻塞的庞萍,因?yàn)閿?shù)據(jù)的insert操作,一旦插入失敗就會(huì)直接報(bào)錯(cuò)忘闻。沒有獲得鎖的線程并不會(huì)進(jìn)入排隊(duì)隊(duì)列钝计,要想再次獲得鎖就要再次觸發(fā)獲得鎖操作。

4齐佳、這把鎖是非重入的葵蒂,同一個(gè)線程在沒有釋放鎖之前無法再次獲得該鎖。因?yàn)閿?shù)據(jù)中數(shù)據(jù)已經(jīng)存在了重虑。

當(dāng)然践付,我們也可以有其他方式解決上面的問題。

數(shù)據(jù)庫是單點(diǎn)缺厉?搞兩個(gè)數(shù)據(jù)庫永高,數(shù)據(jù)之前雙向同步隧土。一旦掛掉快速切換到備庫上。

沒有失效時(shí)間命爬?只要做一個(gè)定時(shí)任務(wù)曹傀,每隔一定時(shí)間把數(shù)據(jù)庫中的超時(shí)數(shù)據(jù)清理一遍。

非阻塞的饲宛?搞一個(gè)while循環(huán)皆愉,直到insert成功再返回成功。

非重入的艇抠?在數(shù)據(jù)庫表中加個(gè)字段幕庐,記錄當(dāng)前獲得鎖的機(jī)器的主機(jī)信息和線程信息,那么下次再獲取鎖的時(shí)候先查詢數(shù)據(jù)庫家淤,如果當(dāng)前機(jī)器的主機(jī)信息和線程信息在數(shù)據(jù)庫可以查到的話异剥,直接把鎖分配給他就可以了。

基于數(shù)據(jù)庫的排它鎖

除了可以通過增刪操作數(shù)據(jù)表中的記錄以外絮重,其實(shí)還可以借助數(shù)據(jù)庫中自帶的鎖來實(shí)現(xiàn)分布式的鎖冤寿。

我們還用剛剛創(chuàng)建的那張數(shù)據(jù)庫表∏嗌耍可以通過數(shù)據(jù)庫的排他鎖來實(shí)現(xiàn)分布式鎖督怜。

在查詢語句后面增加for update,數(shù)據(jù)庫會(huì)在查詢過程中給數(shù)據(jù)庫表增加排他鎖狠角。當(dāng)某條記錄被加上排他鎖之后号杠,其他線程無法再在該行記錄上增加排他鎖。

我們可以認(rèn)為獲得排它鎖的線程即可獲得分布式鎖擎厢,當(dāng)獲取到鎖之后究流,可以執(zhí)行方法的業(yè)務(wù)邏輯,執(zhí)行完方法之后动遭,再通過以下方法解鎖:

public void unlock(){?

? connection.commit();

}

通過connection.commit()操作來釋放鎖芬探。

這種方法可以有效的解決上面提到的無法釋放鎖和阻塞鎖的問題。

阻塞鎖厘惦? for update語句會(huì)在執(zhí)行成功后立即返回偷仿,在執(zhí)行失敗時(shí)一直處于阻塞狀態(tài),直到成功宵蕉。

鎖定之后服務(wù)宕機(jī)酝静,無法釋放?使用這種方式羡玛,服務(wù)宕機(jī)之后數(shù)據(jù)庫會(huì)自己把鎖釋放掉别智。

但是還是無法直接解決數(shù)據(jù)庫單點(diǎn)和可重入問題。

總結(jié):

總結(jié)一下使用數(shù)據(jù)庫來實(shí)現(xiàn)分布式鎖的方式稼稿,這兩種方式都是依賴數(shù)據(jù)庫的一張表薄榛,一種是通過表中的記錄的存在情況確定當(dāng)前是否有鎖存在讳窟,另外一種是通過數(shù)據(jù)庫的排他鎖來實(shí)現(xiàn)分布式鎖。

**數(shù)據(jù)庫實(shí)現(xiàn)分布式鎖的優(yōu)點(diǎn): **直接借助數(shù)據(jù)庫敞恋,容易理解丽啡。

**數(shù)據(jù)庫實(shí)現(xiàn)分布式鎖的缺點(diǎn): **會(huì)有各種各樣的問題,在解決問題的過程中會(huì)使整個(gè)方案變得越來越復(fù)雜硬猫。

操作數(shù)據(jù)庫需要一定的開銷补箍,性能問題需要考慮。

樂觀鎖

樂觀鎖假設(shè)認(rèn)為數(shù)據(jù)一般情況下不會(huì)造成沖突啸蜜,只有在進(jìn)行數(shù)據(jù)的提交更新時(shí)坑雅,才會(huì)檢測(cè)數(shù)據(jù)的沖突情況,如果發(fā)現(xiàn)沖突了盔性,則返回錯(cuò)誤信息

實(shí)現(xiàn)方式:

時(shí)間戳(timestamp)記錄機(jī)制實(shí)現(xiàn):給數(shù)據(jù)庫表增加一個(gè)時(shí)間戳字段類型的字段霞丧,當(dāng)讀取數(shù)據(jù)時(shí)呢岗,將timestamp字段的值一同讀出冕香,數(shù)據(jù)每更新一次,timestamp也同步更新后豫。當(dāng)對(duì)數(shù)據(jù)做提交更新操作時(shí)悉尾,檢查當(dāng)前數(shù)據(jù)庫中數(shù)據(jù)的時(shí)間戳和自己更新前取到的時(shí)間戳進(jìn)行對(duì)比,若相等挫酿,則更新构眯,否則認(rèn)為是失效數(shù)據(jù)。

若出現(xiàn)更新沖突早龟,則需要上層邏輯修改惫霸,啟動(dòng)重試機(jī)制

同樣也可以使用version的方式。

性能對(duì)比

(1) 悲觀鎖實(shí)現(xiàn)方式是獨(dú)占數(shù)據(jù)葱弟,其它線程需要等待壹店,不會(huì)出現(xiàn)修改的沖突,能夠保證數(shù)據(jù)的一致性芝加,但是依賴數(shù)據(jù)庫的實(shí)現(xiàn)硅卢,且在線程較多時(shí)出現(xiàn)等待造成效率降低的問題。一般情況下藏杖,對(duì)于數(shù)據(jù)很敏感且讀取頻率較低的場(chǎng)景将塑,可以采用悲觀鎖的方式

(2) 樂觀鎖可以多線程同時(shí)讀取數(shù)據(jù),若出現(xiàn)沖突蝌麸,也可以依賴上層邏輯修改点寥,能夠保證高并發(fā)下的讀取,適用于讀取頻率很高而修改頻率較少的場(chǎng)景

(3) 由于庫存回寫數(shù)據(jù)屬于敏感數(shù)據(jù)且讀取頻率適中来吩,所以建議使用悲觀鎖優(yōu)化

基于redis的分布式鎖

相比較于基于數(shù)據(jù)庫實(shí)現(xiàn)分布式鎖的方案來說敢辩,基于緩存來實(shí)現(xiàn)在性能方面會(huì)表現(xiàn)的更好一點(diǎn)汉柒。而且很多緩存是可以集群部署的,可以解決單點(diǎn)問題责鳍。

首先碾褂,為了確保分布式鎖可用,我們至少要確保鎖的實(shí)現(xiàn)同時(shí)滿足以下四個(gè)條件:

互斥性历葛。在任意時(shí)刻正塌,只有一個(gè)客戶端能持有鎖。

不會(huì)發(fā)生死鎖恤溶。即使有一個(gè)客戶端在持有鎖的期間崩潰而沒有主動(dòng)解鎖乓诽,也能保證后續(xù)其他客戶端能加鎖。

具有容錯(cuò)性咒程。只要大部分的Redis節(jié)點(diǎn)正常運(yùn)行鸠天,客戶端就可以加鎖和解鎖。

解鈴還須系鈴人帐姻。加鎖和解鎖必須是同一個(gè)客戶端稠集,客戶端自己不能把別人加的鎖給解了。

可以看到饥瓷,我們加鎖就一行代碼:jedis.set(String key, String value, String nxxx, String expx, int time)剥纷,這個(gè)set()方法一共有五個(gè)形參:

第一個(gè)為key,我們使用key來當(dāng)鎖呢铆,因?yàn)閗ey是唯一的晦鞋。

第二個(gè)為value,我們傳的是requestId棺克,很多童鞋可能不明白悠垛,有key作為鎖不就夠了嗎,為什么還要用到value娜谊?原因就是我們?cè)谏厦嬷v到可靠性時(shí)确买,分布式鎖要滿足第四個(gè)條件解鈴還須系鈴人膳凝,通過給value賦值為requestId弹囚,我們就知道這把鎖是哪個(gè)請(qǐng)求加的了,在解鎖的時(shí)候就可以有依據(jù)陶舞。requestId可以使用UUID.randomUUID().toString()方法生成抹剩。

第三個(gè)為nxxx撑帖,這個(gè)參數(shù)我們填的是NX,意思是SET IF NOT EXIST澳眷,即當(dāng)key不存在時(shí)胡嘿,我們進(jìn)行set操作;若key已經(jīng)存在钳踊,則不做任何操作衷敌;

第四個(gè)為expx勿侯,這個(gè)參數(shù)我們傳的是PX,意思是我們要給這個(gè)key加一個(gè)過期的設(shè)置缴罗,具體時(shí)間由第五個(gè)參數(shù)決定助琐。

第五個(gè)為time,與第四個(gè)參數(shù)相呼應(yīng)面氓,代表key的過期時(shí)間兵钮。

總的來說,執(zhí)行上面的set()方法就只會(huì)導(dǎo)致兩種結(jié)果:1. 當(dāng)前沒有鎖(key不存在)舌界,那么就進(jìn)行加鎖操作掘譬,并對(duì)鎖設(shè)置個(gè)有效期,同時(shí)value表示加鎖的客戶端呻拌。2. 已有鎖存在葱轩,不做任何操作。

心細(xì)的童鞋就會(huì)發(fā)現(xiàn)了藐握,我們的加鎖代碼滿足我們可靠性里描述的三個(gè)條件靴拱。首先,set()加入了NX參數(shù)趾娃,可以保證如果已有key存在缭嫡,則函數(shù)不會(huì)調(diào)用成功缔御,也就是只有一個(gè)客戶端能持有鎖抬闷,滿足互斥性。其次耕突,由于我們對(duì)鎖設(shè)置了過期時(shí)間笤成,即使鎖的持有者后續(xù)發(fā)生崩潰而沒有解鎖,鎖也會(huì)因?yàn)榈搅诉^期時(shí)間而自動(dòng)解鎖(即key被刪除)眷茁,不會(huì)發(fā)生死鎖炕泳。最后,因?yàn)槲覀儗alue賦值為requestId上祈,代表加鎖的客戶端請(qǐng)求標(biāo)識(shí)培遵,那么在客戶端在解鎖的時(shí)候就可以進(jìn)行校驗(yàn)是否是同一個(gè)客戶端。由于我們只考慮Redis單機(jī)部署的場(chǎng)景登刺,所以容錯(cuò)性我們暫不考慮籽腕。

錯(cuò)誤實(shí)例:

使用jedis.setnx()和jedis.expire()組合實(shí)現(xiàn)加鎖

public static void wrongGetLock1(Jedis jedis, String lockKey, String requestId, int expireTime) {?

? Long result = jedis.setnx(lockKey, requestId);?

if (result == 1) {

? ? ? // 若在這里程序突然崩潰,則無法設(shè)置過期時(shí)間纸俭,將發(fā)生死鎖? ? ? ? jedis.expire(lockKey, expireTime);?

? ? }

}

setnx()方法作用就是SET IF NOT EXIST皇耗,expire()方法就是給鎖加一個(gè)過期時(shí)間。乍一看好像和前面的set()方法結(jié)果一樣揍很,然而由于這是兩條Redis命令郎楼,不具有原子性万伤,如果程序在執(zhí)行完setnx()之后突然崩潰,導(dǎo)致鎖沒有設(shè)置過期時(shí)間呜袁。那么將會(huì)發(fā)生死鎖敌买。網(wǎng)上之所以有人這樣實(shí)現(xiàn),是因?yàn)榈桶姹镜膉edis并不支持多參數(shù)的set()方法阶界。

解鎖:

首先獲取鎖對(duì)應(yīng)的value值放妈,檢查是否與requestId相等,如果相等則刪除鎖(解鎖)

總結(jié):

可以使用緩存來代替數(shù)據(jù)庫來實(shí)現(xiàn)分布式鎖荐操,這個(gè)可以提供更好的性能芜抒,同時(shí),很多緩存服務(wù)都是集群部署的托启,可以避免單點(diǎn)問題宅倒。并且很多緩存服務(wù)都提供了可以用來實(shí)現(xiàn)分布式鎖的方法,比如redis的setnx方法等屯耸。并且拐迁,這些緩存服務(wù)也都提供了對(duì)數(shù)據(jù)的過期自動(dòng)刪除的支持,可以直接設(shè)置超時(shí)時(shí)間來控制鎖的釋放疗绣。

使用緩存實(shí)現(xiàn)分布式鎖的優(yōu)點(diǎn)

性能好线召,實(shí)現(xiàn)起來較為方便。

使用緩存實(shí)現(xiàn)分布式鎖的缺點(diǎn)

通過超時(shí)時(shí)間來控制鎖的失效時(shí)間并不是十分的靠譜多矮。

基于Zookeeper實(shí)現(xiàn)分布式鎖

基于zookeeper臨時(shí)有序節(jié)點(diǎn)可以實(shí)現(xiàn)的分布式鎖缓淹。大致思想即為:每個(gè)客戶端對(duì)某個(gè)方法加鎖時(shí),在zookeeper上的與該方法對(duì)應(yīng)的指定節(jié)點(diǎn)的目錄下塔逃,生成一個(gè)唯一的

瞬時(shí)有序節(jié)點(diǎn)讯壶。 判斷是否獲取鎖的方式很簡(jiǎn)單,只需要判斷有序節(jié)點(diǎn)中序號(hào)最小的一個(gè)湾盗。 當(dāng)釋放鎖的時(shí)候伏蚊,只需將這個(gè)瞬時(shí)節(jié)點(diǎn)刪除即可。同時(shí)格粪,其可以避免服務(wù)宕機(jī)導(dǎo)

致的鎖無法釋放躏吊,而產(chǎn)生的死鎖問題。

來看下Zookeeper能不能解決前面提到的問題帐萎。

鎖無法釋放比伏?使用Zookeeper可以有效的解決鎖無法釋放的問題,因?yàn)樵趧?chuàng)建鎖的時(shí)候吓肋,客戶端會(huì)在ZK中創(chuàng)建一個(gè)臨時(shí)節(jié)點(diǎn)凳怨,一旦客戶端獲取到鎖之后突然掛掉(

Session連接斷開),那么這個(gè)臨時(shí)節(jié)點(diǎn)就會(huì)自動(dòng)刪除掉。其他客戶端就可以再次獲得鎖肤舞。

非阻塞鎖紫新?使用Zookeeper可以實(shí)現(xiàn)阻塞的鎖,客戶端可以通過在ZK中創(chuàng)建順序節(jié)點(diǎn)李剖,并且在節(jié)點(diǎn)上綁定監(jiān)聽器芒率,一旦節(jié)點(diǎn)有變化,Zookeeper會(huì)通知客戶端篙顺,客戶

端可以檢查自己創(chuàng)建的節(jié)點(diǎn)是不是當(dāng)前所有節(jié)點(diǎn)中序號(hào)最小的偶芍,如果是,那么自己就獲取到鎖德玫,便可以執(zhí)行業(yè)務(wù)邏輯了匪蟀。

不可重入?使用Zookeeper也可以有效的解決不可重入的問題宰僧,客戶端在創(chuàng)建節(jié)點(diǎn)的時(shí)候材彪,把當(dāng)前客戶端的主機(jī)信息和線程信息直接寫入到節(jié)點(diǎn)中,下次想要獲取鎖的

時(shí)候和當(dāng)前最小的節(jié)點(diǎn)中的數(shù)據(jù)比對(duì)一下就可以了琴儿。如果和自己的信息一樣段化,那么自己直接獲取到鎖,如果不一樣就再創(chuàng)建一個(gè)臨時(shí)的順序節(jié)點(diǎn)造成,參與排隊(duì)显熏。

單點(diǎn)問題?使用Zookeeper可以有效的解決單點(diǎn)問題晒屎,ZK是集群部署的喘蟆,只要集群中有半數(shù)以上的機(jī)器存活,就可以對(duì)外提供服務(wù)夷磕。

可以直接使用zookeeper第三方庫Curator客戶端履肃,這個(gè)客戶端中封裝了一個(gè)可重入的鎖服務(wù)。

Zookeeper實(shí)現(xiàn)的分布式鎖其實(shí)存在一個(gè)缺點(diǎn)坐桩,那就是性能上可能并沒有緩存服務(wù)那么高。

因?yàn)槊看卧趧?chuàng)建鎖和釋放鎖的過程中封锉,都要?jiǎng)討B(tài)創(chuàng)建绵跷、銷毀瞬時(shí)節(jié)點(diǎn)來實(shí)現(xiàn)鎖功能。ZK中創(chuàng)建和刪除節(jié)點(diǎn)只能通過Leader服務(wù)器來執(zhí)行成福,然后將數(shù)據(jù)同不到所有的Follower機(jī)器上碾局。

**使用Zookeeper實(shí)現(xiàn)分布式鎖的優(yōu)點(diǎn): **有效的解決單點(diǎn)問題,不可重入問題奴艾,非阻塞問題以及鎖無法釋放的問題净当。實(shí)現(xiàn)起來較為簡(jiǎn)單。

**使用Zookeeper實(shí)現(xiàn)分布式鎖的缺點(diǎn) : **性能上不如使用緩存實(shí)現(xiàn)分布式鎖。 需要對(duì)ZK的原理有所了解像啼。

三種方案的比較

**從理解的難易程度角度(從低到高): **數(shù)據(jù)庫 > 緩存 > Zookeeper

從實(shí)現(xiàn)的復(fù)雜性角度(從低到高)**: **Zookeeper >= 緩存 > 數(shù)據(jù)庫

**從性能角度(從高到低): **緩存 > Zookeeper >= 數(shù)據(jù)庫

**從可靠性角度(從高到低): **Zookeeper > 緩存 > 數(shù)據(jù)庫

參考:

http://wudashan.cn/2017/10/23/Redis-Distributed-Lock-Implement/

http://www.hollischuang.com/archives/1716

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末俘闯,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子忽冻,更是在濱河造成了極大的恐慌真朗,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件僧诚,死亡現(xiàn)場(chǎng)離奇詭異遮婶,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)湖笨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門旗扑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人慈省,你說我怎么就攤上這事肩豁。” “怎么了辫呻?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵清钥,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我放闺,道長(zhǎng)祟昭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任怖侦,我火速辦了婚禮篡悟,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘匾寝。我一直安慰自己搬葬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布艳悔。 她就那樣靜靜地躺著急凰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪猜年。 梳的紋絲不亂的頭發(fā)上抡锈,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音乔外,去河邊找鬼床三。 笑死,一個(gè)胖子當(dāng)著我的面吹牛杨幼,可吹牛的內(nèi)容都是我干的撇簿。 我是一名探鬼主播聂渊,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼四瘫!你這毒婦竟也來了汉嗽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤莲组,失蹤者是張志新(化名)和其女友劉穎诊胞,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锹杈,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡撵孤,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了竭望。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片邪码。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖咬清,靈堂內(nèi)的尸體忽然破棺而出闭专,到底是詐尸還是另有隱情,我是刑警寧澤旧烧,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布影钉,位于F島的核電站,受9級(jí)特大地震影響掘剪,放射性物質(zhì)發(fā)生泄漏平委。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一夺谁、第九天 我趴在偏房一處隱蔽的房頂上張望廉赔。 院中可真熱鬧,春花似錦匾鸥、人聲如沸蜡塌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽馏艾。三九已至,卻和暖如春笆环,著一層夾襖步出監(jiān)牢的瞬間攒至,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工躁劣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人库菲。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓账忘,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鳖擒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

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