openGauss內(nèi)核分析(一):openGauss 多線程架構(gòu)啟動(dòng)過(guò)程詳解

openGauss數(shù)據(jù)庫(kù)自2020年6月30日開(kāi)源以來(lái),吸引了眾多內(nèi)核開(kāi)發(fā)者的關(guān)注。那么openGauss的多線程是如何啟動(dòng)的,一條SQL語(yǔ)句在 SQL引擎,執(zhí)行引擎和存儲(chǔ)引擎的執(zhí)行過(guò)程是怎樣的窑多,酷哥做了一些總結(jié),第一期內(nèi)容主要分析openGauss 多線程架構(gòu)啟動(dòng)過(guò)程鼻疮。

openGauss數(shù)據(jù)庫(kù)是一個(gè)單進(jìn)程多線程的數(shù)據(jù)庫(kù)怯伊,客戶端可以使用JDBC/ODBC/Libpq/Psycopg等驅(qū)動(dòng)程序,向openGauss的主線程(Postmaster)發(fā)起連接請(qǐng)求判沟。


openGauss為什么要使用多線程架構(gòu)

隨著計(jì)算機(jī)領(lǐng)域多核技術(shù)的發(fā)展耿芹,如何充分有效的利用多核的并行處理能力,是每個(gè)服務(wù)器端應(yīng)用程序都必須考慮的問(wèn)題挪哄。由于數(shù)據(jù)庫(kù)服務(wù)器的服務(wù)進(jìn)程或線程間存在著大量數(shù)據(jù)共享和同步吧秕,而多線程可以充分利用多CPU來(lái)并行執(zhí)行多個(gè)強(qiáng)相關(guān)任務(wù),例如執(zhí)行引擎可以充分的利用線程的并發(fā)執(zhí)行以提供性能迹炼。在多線程的架構(gòu)下砸彬,數(shù)據(jù)共享的效率更高,能提高服務(wù)器訪問(wèn)的效率和性能斯入,同時(shí)維護(hù)開(kāi)銷和復(fù)雜度更低砂碉,這對(duì)于提高數(shù)據(jù)庫(kù)系統(tǒng)的并行處理能力非常重要。

多線程的三大主要優(yōu)勢(shì):

優(yōu)勢(shì)一:線程啟動(dòng)開(kāi)銷遠(yuǎn)小于進(jìn)程啟動(dòng)開(kāi)銷刻两。與進(jìn)程相比增蹭,它是一種非常“節(jié)儉”的多任務(wù)操作方式磅摹。在Linux系統(tǒng)下滋迈,啟動(dòng)一個(gè)新的進(jìn)程必須分配給它獨(dú)立的地址空間霎奢,建立眾多的數(shù)據(jù)表來(lái)維護(hù)它的代碼段、堆棧段和數(shù)據(jù)段饼灿,這是一種“昂貴”的多任務(wù)工作方式幕侠。而運(yùn)行于一個(gè)進(jìn)程中的多個(gè)線程,它們彼此之間使用相同的地址空間碍彭,共享大部分?jǐn)?shù)據(jù)晤硕,啟動(dòng)一個(gè)線程所花費(fèi)的空間遠(yuǎn)遠(yuǎn)小于啟動(dòng)一個(gè)進(jìn)程所花費(fèi)的空間。

優(yōu)勢(shì)二:線程間方便的通信機(jī)制:對(duì)不同進(jìn)程來(lái)說(shuō)硕旗,它們具有獨(dú)立的數(shù)據(jù)空間窗骑,要進(jìn)行數(shù)據(jù)的傳遞只能通過(guò)通信的方式進(jìn)行,這種方式不僅費(fèi)時(shí)漆枚,而且很不方便。線程則不然抵知,由于同一進(jìn)程下的線程之間共享數(shù)據(jù)空間墙基,所以一個(gè)線程的數(shù)據(jù)可以直接為其他線程所用,這不僅快捷刷喜,而且方便残制。

優(yōu)勢(shì)三:線程切換開(kāi)銷小于進(jìn)程切換開(kāi)銷,對(duì)于Linux系統(tǒng)來(lái)講掖疮,進(jìn)程切換分兩步:1.切換頁(yè)目錄以使用新的地址空間初茶;2.切換內(nèi)核棧和硬件上下文。對(duì)線程切換浊闪,第1步是不需要做的恼布,第2步是進(jìn)程和線程都要做的,所以明顯線程切換開(kāi)銷小搁宾。

openGauss主要線程有哪些

后臺(tái)線程功能介紹

Postmaster

主線程

入口函數(shù)PostmasterMain折汞,主要負(fù)責(zé)內(nèi)存、全局信息盖腿、信號(hào)爽待、線程池等的初始化,啟動(dòng)輔助線程并監(jiān)控線程狀態(tài)翩腐,循環(huán)**接收新的連接

Walwriter

日志寫線程

入口函數(shù)WalWriterMain鸟款,將內(nèi)存的預(yù)寫日志頁(yè)數(shù)據(jù)刷新到預(yù)寫日志文件中,保證已提交的事物永久記錄茂卦,不會(huì)丟失

Startup

數(shù)據(jù)庫(kù)啟動(dòng)線程

入口函數(shù)StartupProcessMain何什,數(shù)據(jù)庫(kù)啟動(dòng)時(shí)Postmaster主線程拉起的第一個(gè)子線程,主要完成數(shù)據(jù)庫(kù)的日志REDO(重做)操作疙筹,進(jìn)行數(shù)據(jù)庫(kù)的恢復(fù)富俄。日志REDO操作結(jié)束禁炒,數(shù)據(jù)庫(kù)完成恢復(fù)后,如果不是備機(jī)霍比,Startup線程就退出了幕袱。如果是備機(jī),那么Startup線程一直在運(yùn)行悠瞬,REDO備機(jī)接收到新的日志

Bgwriter

后臺(tái)數(shù)據(jù)寫線程

入口函數(shù)BackgroundWriterMain们豌,對(duì)共享緩沖區(qū)的臟頁(yè)數(shù)據(jù)進(jìn)行下盤

PageWriter入口函數(shù)ckpt_pagewriter_main,將臟頁(yè)數(shù)據(jù)拷貝至雙寫區(qū)域并落盤

Checkpointer

檢查點(diǎn)線程

入口函數(shù)CheckpointerMain浅妆,周期性檢查點(diǎn)望迎,所有數(shù)據(jù)文件被更新,將數(shù)據(jù)臟頁(yè)刷新到磁盤凌外,確保數(shù)據(jù)庫(kù)一致辩尊;崩潰回復(fù)后,做過(guò)checkpointer更改不需要從預(yù)寫日志中恢復(fù)

StatCollector

統(tǒng)計(jì)線程

入口函數(shù)PgstatCollectorMain康辑,統(tǒng)計(jì)信息摄欲,包括對(duì)象、sql疮薇、會(huì)話胸墙、鎖等,保存到pgstat.stat文件中按咒,用于性能迟隅、故障、狀態(tài)分析

WalSender

日志發(fā)送線程

入口函數(shù)WalSenderMain励七,主機(jī)發(fā)送預(yù)寫日志

WalReceiver

日志接收線程

入口函數(shù)WalReceiverMain智袭,備機(jī)接收預(yù)寫日志

Postgres

業(yè)務(wù)處理線程

入口函數(shù)PostgresMain:處理客戶端連接請(qǐng)求,執(zhí)行相關(guān)SQL業(yè)務(wù)

數(shù)據(jù)庫(kù)啟動(dòng)后呀伙,可以通過(guò)操作系統(tǒng)命令ps查看線程信息(進(jìn)程號(hào)為17012)


openGauss啟動(dòng)過(guò)程

下面主要介紹openGauss數(shù)據(jù)庫(kù)的啟動(dòng)過(guò)程补履,包括主線程,輔助線程及業(yè)務(wù)處理線程的啟動(dòng)過(guò)程

gs_ctl啟動(dòng)數(shù)據(jù)庫(kù)

gs_ctl是openGauss提供的數(shù)據(jù)庫(kù)服務(wù)控制工具剿另,可以用來(lái)啟停數(shù)據(jù)庫(kù)服務(wù)和查詢數(shù)據(jù)庫(kù)狀態(tài)箫锤。主要供數(shù)據(jù)庫(kù)管理模塊調(diào)用,啟動(dòng)數(shù)據(jù)庫(kù)使用如下命令

gs_ctlstart-D /opt/software/data-Z single_node

gs_ctl的入口函數(shù)在“src/bin/pg_ctl/pg_ctl.cpp”雨女,gs_ctl進(jìn)程fork一個(gè)進(jìn)程來(lái)運(yùn)行 gaussdb進(jìn)程谚攒,通過(guò)shell命令啟動(dòng)。


上圖中的cmd為“/opt/software/openGauss/bin/gaussdb -D /opt/software/openGauss/data”氛堕,進(jìn)入到數(shù)據(jù)庫(kù)運(yùn)行調(diào)用的第一個(gè)函數(shù)main函數(shù)馏臭,在“src/gausskernel/process/main/main.cpp”文件中,在main.cpp文件中,主要完成實(shí)例Context(上下文)的初始化括儒、本地化設(shè)置绕沈,根據(jù)main.cpp文件的入口參數(shù)調(diào)用BootStrapProcessMain函數(shù)、GucInfoMain函數(shù)帮寻、PostgresMain函數(shù)和PostmasterMain函數(shù)乍狐。BootStrapProcessMain函數(shù)和PostgresMain函數(shù)是在initdb場(chǎng)景下初始化數(shù)據(jù)庫(kù)使用的。GucInfoMain函數(shù)作用是顯示GUC(grand unified configuration固逗,配置參數(shù)浅蚪,在數(shù)據(jù)庫(kù)中指的是運(yùn)行參數(shù))參數(shù)信息。正常的數(shù)據(jù)庫(kù)啟動(dòng)會(huì)進(jìn)入PostmasterMain函數(shù)烫罩。下面對(duì)這個(gè)函數(shù)進(jìn)行更詳細(xì)的介紹惜傲。


MemoryContextInit:內(nèi)存上下文系統(tǒng)初始化,主要完成對(duì)ThreadTopMemoryContext贝攒,ErrorContext盗誊,AlignContext和ProfileLogging等全局變量的初始化

pg_perm_setlocale:設(shè)置程序語(yǔ)言環(huán)境相關(guān)的全局變量

check_root:?確認(rèn)程序運(yùn)行者無(wú)操作系統(tǒng)的root權(quán)限,防止的意外文件覆蓋等問(wèn)題

如果gaussdb后的第一個(gè)參數(shù)是—boot,則進(jìn)行數(shù)據(jù)庫(kù)初始化饿这,如果gaussdb后的第一個(gè)參數(shù)是--single浊伙,則調(diào)用PostgresMain(),進(jìn)入(本地)單用戶版服務(wù)端程序长捧。之后,與普通服務(wù)器端線程類似吻贿,循環(huán)等待用戶輸入SQL語(yǔ)句串结,直至用戶輸入EOF(Ctrl+D),退出程序舅列。如果沒(méi)有指定額外啟動(dòng)選項(xiàng)肌割,程序進(jìn)入PostmasterMain函數(shù),開(kāi)始一系列服務(wù)器端的正常初始化工作帐要。

PostmasterMain函數(shù)

下面具體介紹PostmasterMain把敞。


設(shè)置線程號(hào)相關(guān)的全局變量MyProcPid、PostmasterPid榨惠、MyProgName和程序運(yùn)行環(huán)境相關(guān)的全局變量IsPostmasterEnvironment

調(diào)用postmaster_mem_cxt = AllocSetContextCreate(t_thrd.top_mem_cxt,...)奋早,在目前線程的top_mem_cxt下創(chuàng)建postmaster_mem_cxt全局變量和相應(yīng)的內(nèi)存上下文

MemoryContextSwitchTo(postmaster_mem_cxt)切換到postmaster_mem_cxt內(nèi)存上下文

調(diào)用getInstallationPaths(),設(shè)置my_exec_path(一般即為gaussdb可執(zhí)行文件所在路徑)

調(diào)用InitializeGUCOptions()赠橙,根據(jù)代碼中各個(gè)GUC參數(shù)的默認(rèn)值生成ConfigureNamesBool耽装、ConfigureNamesInt、ConfigureNamesReal期揪、ConfigureNamesString掉奄、ConfigureNamesEnum等?GUC參數(shù)的全局變量數(shù)組,以及統(tǒng)一管理GUC參數(shù)的guc_variables凤薛、num_guc_variables姓建、size_guc_variables全局變量诞仓,并設(shè)置與具體操作系統(tǒng)環(huán)境相關(guān)的GUC參數(shù)

while (opt = ...)?SetConfigOption,?若在啟動(dòng)gaussdb時(shí)用指定了非默認(rèn)的GUC參數(shù),則在此時(shí)加載至上一步中創(chuàng)建的全局變量中

調(diào)用checkDataDir()速兔,確認(rèn)數(shù)據(jù)庫(kù)安裝成功以及PGDATA目錄的有效性

調(diào)用CreateDataDirLockFile()墅拭,創(chuàng)建數(shù)據(jù)目錄的鎖文件

調(diào)用process_shared_preload_libraries(),處理預(yù)加載庫(kù)

為每個(gè)ListenSocket創(chuàng)建**

reset_shared憨栽,設(shè)置共享內(nèi)存和信號(hào)帜矾,主要包括頁(yè)面緩存池、各種鎖緩存池屑柔、WAL日志緩存池屡萤、事務(wù)日志緩存池、事務(wù)(號(hào))概況緩存池掸宛、各后臺(tái)線程(鎖使用)概況緩存池死陆、各后臺(tái)線程等待和運(yùn)行狀態(tài)緩存池、兩階段狀態(tài)緩存池唧瘾、檢查點(diǎn)緩存池措译、WAL日志復(fù)制和接收緩存池、數(shù)據(jù)頁(yè)復(fù)制和接收緩存池等饰序。在后續(xù)階段創(chuàng)建出的客戶端后臺(tái)線程以及各個(gè)輔助線程均使用該共享內(nèi)存空間领虹,不再單獨(dú)開(kāi)辟

將啟動(dòng)時(shí)手動(dòng)設(shè)置的GUC參數(shù)以文件形式保存下來(lái),以供后續(xù)后臺(tái)服務(wù)端線程啟動(dòng)時(shí)使用

為不同信號(hào)設(shè)置handler

調(diào)用pgstat_init()求豫,初始化狀態(tài)收集子系統(tǒng)塌衰;

調(diào)用load_hba(),加載pg_hba.conf文件蝠嘉,該文件記錄了允許連接(指定或全部)數(shù)據(jù)庫(kù)的客戶端物理機(jī)的地址和端口最疆;調(diào)用load_ident(),加載pg_ident.conf文件蚤告,該文件記錄了操作系統(tǒng)用戶名與數(shù)據(jù)庫(kù)系統(tǒng)用戶名的對(duì)應(yīng)關(guān)系努酸,以便后續(xù)處理客戶端連接時(shí)的身份認(rèn)證

調(diào)用?StartupPID = initialize_util_thread(STARTUP),進(jìn)行數(shù)據(jù)一致性校驗(yàn)杜恰。對(duì)于服務(wù)端主機(jī)來(lái)說(shuō)获诈,查看pg_control文件,若上次關(guān)閉狀態(tài)為DB_SHUTDOWNED且recovery.conf文件沒(méi)有指定進(jìn)行恢復(fù)箫章,則認(rèn)為數(shù)據(jù)一致性成立烙荷;否則,根據(jù)pg_control中檢查點(diǎn)的redo位置或者recovery.conf文件中指定的位置檬寂,讀取WAL日志或歸檔日志進(jìn)行replay(回放)终抽,直至數(shù)據(jù)達(dá)到預(yù)期的一致性狀,主要函數(shù)StartupXLOG

最后進(jìn)入ServerLoop()函數(shù),循環(huán)響應(yīng)客戶端連接請(qǐng)求

ServerLoop函數(shù)

下面來(lái)講ServerLoop函數(shù)主流程


調(diào)用gs_signal_setmask(&UnBlockSig, NULL)和gs_signal_unblock_sigusr2()昼伴,使得線程可以響應(yīng)用戶或其它線程的匾旭、指定的信號(hào)集

每隔PM_POLL_TIMEOUT_MINUTE時(shí)間修改一次socket文件和socket鎖文件的訪問(wèn)和修改時(shí)間,以免**作系統(tǒng)淘汰

判斷線程狀態(tài)(pmState)圃郊,若為PM_WAIT_DEAD_END价涝,則休眠100毫秒,并且不接收任何連接持舆;否則色瘩,通過(guò)系統(tǒng)調(diào)用poll()或select()來(lái)阻塞地讀取**端口上傳入的數(shù)據(jù),最長(zhǎng)阻塞時(shí)間PM_POLL_TIMEOUT_SECOND

調(diào)用gs_signal_setmask(&BlockSig, NULL)和gs_signal_block_sigusr2()不再接收外源信號(hào)

判斷poll()或select()函數(shù)的返回值逸寓,若小于零居兆,**出錯(cuò),服務(wù)端進(jìn)程退出竹伸;若大于零泥栖,則創(chuàng)建連接ConnCreate(),并進(jìn)入后臺(tái)服務(wù)線程啟動(dòng)流程BackendStartup()勋篓。對(duì)于父線程吧享,即postmaster線程,在結(jié)束B(niǎo)ackendStartup()的調(diào)用以后譬嚣,會(huì)調(diào)用ConnFree()钢颂,清除連接信息;若poll()或select()的返回值為零拜银,即沒(méi)有信息傳入甸陌,則不進(jìn)行任何操作

調(diào)用ADIO_RUN()、ADIO_END()?盐股,若AioCompleters沒(méi)有啟動(dòng),則啟動(dòng)之

檢查各個(gè)輔助線程的線程號(hào)是否為零耻卡,若為零疯汁,則調(diào)用initialize_util_thread啟動(dòng)

以非線程池模式為例,介紹線程的啟動(dòng)邏輯卵酪。BackendStartup函數(shù)是通過(guò)調(diào)用initialize_worker_thread(WORKE幌蚊,port)創(chuàng)建一個(gè)后臺(tái)線程處理客戶請(qǐng)求。后臺(tái)線程的啟動(dòng)函數(shù)initialize_util_thread和工作線程的啟動(dòng)函數(shù)initialize_worker_thread溃卡,最后都是調(diào)用initialize_thread函數(shù)完成線程的啟動(dòng)溢豆。


initialize_thread函數(shù)調(diào)用gs_thread_create函數(shù)創(chuàng)建線程,調(diào)用InternalThreadFunc函數(shù)處理線程

ThreadIdinitialize_thread(ThreadArg* thr_argv){gs_thread_tthread;interror_code = gs_thread_create(&thread, InternalThreadFunc,1, (void*)thr_argv);if(error_code !=0) {ereport(LOG,(errmsg("can not fork thread[%s], errcode:%d, %m",GetThreadName(thr_argv->m_thd_arg.role), error_code)));gs_thread_release_args_slot(thr_argv);returnInvalidTid;}returngs_thread_id(thread);}

InternalThreadFunc函數(shù)根據(jù)角色調(diào)用GetThreadEntry函數(shù)瘸羡,GetThreadEntry函數(shù)直接以角色為下標(biāo)漩仙,返回對(duì)應(yīng)GaussdbThreadEntryGate數(shù)組對(duì)應(yīng)的元素。數(shù)組的元素是處理具體任務(wù)的回調(diào)函數(shù)指針,指針指向的函數(shù)為GaussDbThreadMain

staticvoid*InternalThreadFunc(void* args){knl_thread_arg* thr_argv = (knl_thread_arg*)args;gs_thread_exit((GetThreadEntry(thr_argv->role))(thr_argv));return(void*)NULL;}GaussdbThreadEntryGetThreadEntry(knl_thread_role role){Assert(role > MASTER && role < THREAD_ENTRY_BOUND);returnGaussdbThreadEntryGate[role];}staticGaussdbThreadEntry GaussdbThreadEntryGate[] = {GaussDbThreadMain,GaussDbThreadMain,GaussDbThreadMain,GaussDbThreadMain,......};

在GaussDbThreadMain函數(shù)中队他,首先初始化線程基本信息卷仑,Context和信號(hào)處理函數(shù),接著就是根據(jù)thread_role角色的不同調(diào)用不同角色的處理函數(shù)麸折,進(jìn)入各個(gè)線程的main函數(shù)锡凝,角色為WORKER會(huì)進(jìn)入PostgresMain函數(shù),下面具體介紹PostgresMain函數(shù)

PostgresMain函數(shù)

process_postgres_switches(),加載傳入的啟動(dòng)選項(xiàng)和GUC參數(shù)

為不同信號(hào)設(shè)置handler

調(diào)用sigdelset(&BlockSig, SIGQUIT)垢啼,允許響應(yīng)SIGQUIT信號(hào)

調(diào)用BaseInit()窜锯,初始化存儲(chǔ)管理系統(tǒng)和頁(yè)面緩存池計(jì)數(shù)

調(diào)用on_shmem_exit(),設(shè)置線程退出前需要進(jìn)行的內(nèi)存清理動(dòng)作芭析。這些清理動(dòng)作構(gòu)成一個(gè)鏈表(on_shmem_exit_list全局變量)锚扎,每次調(diào)用該函數(shù)都向鏈表尾端添加一個(gè)節(jié)點(diǎn),鏈表長(zhǎng)度由on_shmem_exit_index記錄放刨,且不可超過(guò)MAX_ON_EXITS宏工秩。在線程退出時(shí),從后往前調(diào)用各個(gè)節(jié)點(diǎn)中的動(dòng)作(函數(shù)指針)进统,完成清理工作

調(diào)用gs_signal_setmask (&UnBlockSig)助币,設(shè)置屏蔽的信號(hào)類型

調(diào)用InitBackendWorker進(jìn)行統(tǒng)計(jì)系統(tǒng)初始化、syscache初始化工作

BeginReportingGUCOptions如有需要?jiǎng)t打印GUC參數(shù)

調(diào)用on_proc_exit()螟碎,設(shè)置線程退出前需要進(jìn)行的線程清理動(dòng)作眉菱。設(shè)置和調(diào)用機(jī)制與on_shmem_exit()類似

調(diào)用process_local_preload_libraries(),處理GUC參數(shù)設(shè)定后的預(yù)加載庫(kù)

AllocSetContextCreate創(chuàng)建MessageContext掉分、RowDescriptionContext俭缓、MaskPasswordCtx上下文

調(diào)用sigsetjmp(),設(shè)置longjump點(diǎn)酥郭,若后續(xù)查詢執(zhí)行中出錯(cuò)华坦,在某些情況下可以返回此處重新開(kāi)始

調(diào)用gs_signal_unblock_sigusr2(),允許線程響應(yīng)指定的信號(hào)集

然后進(jìn)入for循環(huán)不从,進(jìn)行查詢執(zhí)行? ? ? ? ? ?


設(shè)置全局變量DoingCommandRead = true

調(diào)用ReadCommand(),讀取客戶端SQL語(yǔ)句

設(shè)置全局變量DoingCommandRead=false

若在上述過(guò)程中收到SIGHUP信號(hào)惜姐,表示線程需要重新加載修改過(guò)的postgresql.conf配置文件

進(jìn)入switch (firstchar),根據(jù)接收到的信息進(jìn)行分支判斷

調(diào)用pgstat_report_activity()椿息、pgstat_report_waitstatus()歹袁,告訴統(tǒng)計(jì)系統(tǒng)后臺(tái)線程正處于idle狀態(tài)

思考如何新增一個(gè)輔助線程

參考其他線程完成

涉及修改文件Postmaster.cpp

涉及修改函數(shù)GaussdbThreadGate – 定義

Serverloop – 啟動(dòng)線程

Reaper – 回收線程

GaussDBThreadMain – 入口函數(shù)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市寝优,隨后出現(xiàn)的幾起案子条舔,更是在濱河造成了極大的恐慌,老刑警劉巖乏矾,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件孟抗,死亡現(xiàn)場(chǎng)離奇詭異迁杨,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)夸浅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門仑最,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人帆喇,你說(shuō)我怎么就攤上這事警医。” “怎么了坯钦?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵预皇,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我婉刀,道長(zhǎng)吟温,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任突颊,我火速辦了婚禮鲁豪,結(jié)果婚禮上酌呆,老公的妹妹穿的比我還像新娘洗显。我一直安慰自己,他們只是感情好箍邮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布棒动。 她就那樣靜靜地躺著糙申,像睡著了一般。 火紅的嫁衣襯著肌膚如雪船惨。 梳的紋絲不亂的頭發(fā)上柜裸,一...
    開(kāi)封第一講書(shū)人閱讀 51,590評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音粱锐,去河邊找鬼疙挺。 笑死,一個(gè)胖子當(dāng)著我的面吹牛怜浅,可吹牛的內(nèi)容都是我干的衔统。 我是一名探鬼主播,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼海雪,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了舱殿?” 一聲冷哼從身側(cè)響起奥裸,我...
    開(kāi)封第一講書(shū)人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎沪袭,沒(méi)想到半個(gè)月后湾宙,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體樟氢,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年侠鳄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了埠啃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡伟恶,死狀恐怖碴开,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情博秫,我是刑警寧澤潦牛,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站挡育,受9級(jí)特大地震影響巴碗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜即寒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一橡淆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧母赵,春花似錦逸爵、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至施绎,卻和暖如春溯革,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背谷醉。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工致稀, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人俱尼。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓抖单,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親遇八。 傳聞我的和親對(duì)象是個(gè)殘疾皇子矛绘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355

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