從庫 MTS 多線程并行回放(二)

本文作者:高鵬昧廷,歡迎訂閱他的簡書專欄

本節(jié)包含一個筆記堪嫂,鏈接如下:

http://www.reibang.com/p/e920a6d33005


這一節(jié)會先描述 MTS 的工作線程執(zhí)行 Event 的大概流程。然后重點描述一下 MTS 中檢查點的概念木柬。在后面的第 25 節(jié)我們可以看到皆串,MTS 的異常恢復很多情況下需要依賴這個檢查點眉枕,從檢查點位置開始掃描 relay log 做恢復操作恶复,但是在 GTID AUTO_POSITION MODE 模式且設(shè)置了 recovery_relay_log=1 的情況下這種依賴將會弱化怜森。

一、工作線程執(zhí)行 Event

前面我們已經(jīng)討論了協(xié)調(diào)線程分發(fā) Event 的規(guī)則谤牡,實際上協(xié)調(diào)線程只是將 Event 分發(fā)到了工作線程的執(zhí)行隊列中副硅。那么工作線程執(zhí)行 Event 就需要從執(zhí)行隊列中拿出這些 Event,然后進行執(zhí)行拓哟。整個過程可以參考函數(shù) slave_worker_exec_job_group想许。因為這個流程比較簡單伶授,因此就不需要畫圖了断序,但是我們需要關(guān)注一些點如下:

(1)從執(zhí)行隊列中讀取 Event。注意這里如果執(zhí)行隊列中沒有 Event 那么就進入空閑等待糜烹,也就是工作線程處于無事可做的狀態(tài)违诗,等待狀態(tài)為 ‘Waiting for an event from Coordinator’。

(2)如果執(zhí)行到 XID_EVENT 那么說明事務(wù)已經(jīng)結(jié)束了那么需要完成內(nèi)存信息更新操作疮蹦≈畛伲可參考 Slave_worker::slave_worker_exec_event 和 Xid_apply_log_event::do_apply_event_worker 函數(shù)。更新內(nèi)存相關(guān)信息可參考函數(shù) commit_positions 函數(shù)愕乎。下面是一些更新的信息阵苇,我們可以看到和 slave_worker_info 表中的信息基本一致,如下:

1感论、更新當前信息
strmake(group_relay_log_name, ptr_g->group_relay_log_name,
sizeof(group_relay_log_name) - 1);
group_relay_log_pos= ev->future_event_relay_log_pos;
set_group_master_log_pos(ev->common_header->log_pos);
set_group_master_log_name(c_rli->get_group_master_log_name());
    
2绅项、將檢查點信息進行寫入:
strmake(checkpoint_relay_log_name, ptr_g-
>checkpoint_relay_log_name,sizeof(checkpoint_relay_log_name) - 1);
checkpoint_relay_log_pos= ptr_g->checkpoint_relay_log_pos;
strmake(checkpoint_master_log_name, ptr_g-
>checkpoint_log_name,sizeof(checkpoint_master_log_name) - 1);
checkpoint_master_log_pos= ptr_g->checkpoint_log_pos;
    
3、設(shè)置GAQ序號:
checkpoint_seqno= ptr_g->checkpoint_seqno;
更新整個BITMAP比肄,可能已經(jīng)由檢查點進行GAQ出隊:
for (uint pos= ptr_g->shifted; pos < c_rli->checkpoint_group; pos++)
//重新設(shè)置位圖 因為checkpoint已經(jīng)
{
//ptr_g->shifted是GAQ中出隊的事務(wù)個數(shù)
if (bitmap_is_set(&group_shifted, pos))
//這里就需要偏移掉出隊的事務(wù)快耿,恢復已經(jīng)不需要了
bitmap_set_bit(&group_executed, pos - ptr_g->shifted);
}
4、設(shè)置位圖:
bitmap_set_bit(&group_executed, ptr_g->checkpoint_seqno);
//在本次事務(wù)相應的位置設(shè)置為1

(3)如果執(zhí)行到 XID_EVENT 那么說明事務(wù)已經(jīng)結(jié)束了那么需要完成內(nèi)存信息的持久化芳绩,即強制刷內(nèi)存信息持久化到 slave_worker_info 表中(relay_log_info_repository 設(shè)置為TABLE)掀亥。可參考函數(shù) commit_positions 函數(shù)妥色,如下:

if ((error= w->commit_positions(this, ptr_group,
w->is_transactional())))

(4)如果執(zhí)行到 XID_EVENT 還需要進行事務(wù)的提交操作搪花,也就是進行 Innodb 層事務(wù)的提交。

從上面我們可以看到 MTS 中每次事務(wù)的提交并不會更新 slave_relay_log_info 表嘹害,而是進行 slave_worker_info 表的更新撮竿,將最新的信息寫入到 slave_worker_info 表中。

我們前面也說過 SQL 線程已經(jīng)蛻變?yōu)閰f(xié)調(diào)線程吼拥,那么 slave_relay_log_info 表什么時候更新呢倚聚?下面我們就能看到 slave_relay_log_info 表的更新實際上由協(xié)調(diào)線程在做完檢查點之后更新。

二凿可、MTS 中檢查點中的重要概念

總的說來 MTS 中的檢查點是 MTS 進行異郴笳郏恢復的起點授账。實際上就是代表到這個位置之前(包含自身)事務(wù)都是已經(jīng)在從庫執(zhí)行過了,但之后的事務(wù)可能執(zhí)行完成了也可能沒有執(zhí)行完成惨驶。檢查點由協(xié)調(diào)線程進行白热。

(1)協(xié)調(diào)線程的 GAQ 隊列

前面我們已經(jīng)知道 MTS 中為每個工作線程維護了一個 Event 的分發(fā)隊列。除此之外協(xié)調(diào)線程還維護了一個非常的重要的隊列 GAQ粗卜,它是一個環(huán)形隊列屋确。下面是源碼中的定義:

/*
master-binlog ordered queue of Slave_job_group descriptors of groups
that are under processing. The queue size is @c checkpoint_group. Group assigned
*/
Slave_committed_queue *gaq;

每次協(xié)調(diào)線程分發(fā)事務(wù)的時候都會將事務(wù)記錄到 GAQ 隊列中,因此 GAQ 中事務(wù)的順序總是和 relay log 文件中事務(wù)的順序一致的续扔。檢查點正是作用在 GAQ 隊列上的攻臀,每次檢查點的位置稱為 LWM,還記得上一節(jié)我叫大家先忽略的 LWM 嗎纱昧?就是這個刨啸。源碼中定義也正是如此,它在 GAQ 隊列中進行維護识脆。如下:

/*
The last checkpoint time Low-Water-Mark
*/
Slave_job_group lwm;

在 GAQ 隊列中還維護有一個叫做 checkpoint_seqno 的序號设联,它是最后一次檢查點以來每個分配事務(wù)的序號,下面是源碼中的定義:

uint checkpoint_seqno;  // counter of groups executed after the most recent CP

在協(xié)調(diào)線程讀取到 GTIDLOGEVENT 后為其分配序號灼捂,記作 checkpoint_seqno离例,如下:

rli->checkpoint_seqno++;//增加seqno

當協(xié)調(diào)線程進行檢查點的時候 checkpoint_seqno 序號會減去出隊的事務(wù)數(shù)量,如下:

checkpoint_seqno= checkpoint_seqno - shift; //這里減去出隊的事務(wù)

在 MTS 異诚こ恚恢復的時候也會用到這個序號宫蛆,每個工作線程會通過這個序號來確認本工作線程執(zhí)行事務(wù)的上限,如下:

for (uint i= (w->checkpoint_seqno + 1) - recovery_group_cnt,
j= 0; i <= w->checkpoint_seqno; i++, j++)
{
if (bitmap_is_set(&w->group_executed, i))
//如果這一位 已經(jīng)設(shè)置
{
DBUG_PRINT("mts", ("Setting bit %u.", j));
bitmap_fast_test_and_set(groups, j);
//那么GTOUPS 這個 bitmap中應該設(shè)置偎球,最終GTOUPS會包含全的需要恢復的事務(wù)
}
}

關(guān)于詳細的異橙髟恢復流程將在第 25 節(jié)描述。

(2)工作線程的 Bitmap

有了 GAQ 隊列和檢查點就知道異乘バ酰恢復開始的位置了袍冷。但是我們并不知道每一個工作線程都完成了哪些事務(wù),哪些又沒有執(zhí)行完成猫牡,因此就不能確認哪些事務(wù)需要恢復胡诗。在 MTS 中并行回放事務(wù)的提交并不是按分發(fā)順序的進行的,某些大事務(wù)(或者其他原因比鎖堵塞)可能遲遲不能提交淌友,而一些小事務(wù)卻會很快提交完成煌恢。這些遲遲不能提交的事務(wù)就成為了所謂的 'gap',如果使用了 GTID 那么在查看已經(jīng)執(zhí)行 GTID SET 的時候可能出現(xiàn)一些‘空洞’震庭,為了防止 'gap' 的發(fā)生通常需要設(shè)置參數(shù) slave_preserve_commit_order瑰抵。下一節(jié)我們將會看到這種‘空洞’以及 slave_preserve_commit_order 的作用。但是如果要設(shè)置了 slave_preserve_commit_order 參數(shù)就需要開啟從庫記錄 binary log 的功能器联,因此必須開啟 log_slave_updates 參數(shù)二汛。下面是源碼的判斷:

if (opt_slave_preserve_commit_order && rli->opt_slave_parallel_workers > 0 &&
opt_bin_log && opt_log_slave_updates)
commit_order_mngr= new Commit_order_manager(rli->opt_slave_parallel_workers);
//order commit 管理器

這里先提前說一下 MTS 恢復的會有兩個關(guān)鍵階段:

  • 掃描階段

    通過掃描檢查點以后的 relay log婿崭。通過每個工作線程的 Bitmap 區(qū)分出哪些事務(wù)已經(jīng)執(zhí)行完成,哪些事務(wù)沒有執(zhí)行完成肴颊,并且匯總形成恢復 Bitmap氓栈,同時得到需要恢復的事務(wù)總量。

  • 執(zhí)行階段

    通過這個匯總的恢復 Bitmap婿着,將這些沒有執(zhí)行完成事務(wù)讀取 relay log 再次執(zhí)行授瘦。

這個 Bitmap 位圖和 GAQ 中的事務(wù)一一對應。當執(zhí)行 XID_EVENT 完成提交后這一位將會被設(shè)置為 ‘1’竟宋。

(3)協(xié)調(diào)線程信息的持久化

這個已經(jīng)在前面提到過提完,實際上每次進行檢查點的時候都需要將檢查點的位置固化到 slave_relay_log_info 表中(relay_log_info_repository 設(shè)置為 TABLE)。因此 slave_relay_log_info 中存儲的實際上不是實時的信息而是檢查點的信息袜硫。下面就是 slave_relay_log_info 表的表結(jié)構(gòu):

mysql> desc slave_relay_log_info;
+-------------------+---------------------+------+-----+---------+-------+
| Field             | Type                | Null | Key | Default | Extra |
+-------------------+---------------------+------+-----+---------+-------+
| Number_of_lines   | int(10) unsigned    | NO   |     | NULL    |       |
| Relay_log_name    | text                | NO   |     | NULL    |       |
| Relay_log_pos     | bigint(20) unsigned | NO   |     | NULL    |       |
| Master_log_name   | text                | NO   |     | NULL    |       |
| Master_log_pos    | bigint(20) unsigned | NO   |     | NULL    |       |
| Sql_delay         | int(11)             | NO   |     | NULL    |       |
| Number_of_workers | int(10) unsigned    | NO   |     | NULL    |       |
| Id                | int(10) unsigned    | NO   |     | NULL    |       |
| Channel_name      | char(64)            | NO   | PRI | NULL    |       |
+-------------------+---------------------+------+-----+---------+-------+

與此同時 show slave status 中的某些信息也是檢查點的內(nèi)存信息氯葬。下面的信息將是來自檢查點:

  • Relay_Log_File:最新一次檢查點的 relay log 文件名挡篓。

  • Relay_Log_Pos:最新一次檢查點的 relay log 位點婉陷。

  • Relay_Master_Log_File:最新一次檢查點的主庫 binary log 文件名。

  • Exec_Master_Log_Pos:最新一次檢查點的主庫 binary log 位點官研。

  • Seconds_Behind_Master:根據(jù)檢查點指向事務(wù)的提交時間計算的延遲秽澳。

需要注意的是我們的 GTID 模塊獨立在這一套理論之外,在第 3 節(jié)我們講 GTID 模塊的初始化的時候我們就說過 GTID 模塊的初始化是在從庫信息初始化之前就完成了戏羽。因此在做 MTS 異车I瘢恢復的時候使用 GTID AUTO_POSITION MODE 模式將會變得更加簡單和安全,細節(jié)將在第 25 節(jié)描述始花。

(4)工作線程信息的持久化

工作線程的信息就持久化在 slave_worker_info 表中妄讯,前面我們描述工作線程執(zhí)行 Event 注意點的時候已經(jīng)做了相應的描述。執(zhí)行 XID_EVENT 完成事務(wù)提交之后會將信息寫入到 slave_worker_info 表中(relay_log_info_repository 設(shè)置為 TABLE)酷宵。其中包括信息:

  • Relay_log_name:工作線程最后一個提交事務(wù)的 relay log 文件名亥贸。

  • Relay_log_pos:工作線程最后一個提交事務(wù)的 relay log 位點。

  • Master_log_name:工作線程最后一個提交事務(wù)的主庫 binary log 文件名浇垦。

  • Master_log_pos:工作線程最后一個提交事務(wù)的主庫 binary log 文件位點炕置。

  • Checkpoint_relay_log_name:工作線程最后一個提交事務(wù)對應檢查點的 relay log 文件名。

  • Checkpoint_relay_log_pos:工作線程最后一個提交事務(wù)對應檢查點的 relay log 位點男韧。

  • Checkpoint_master_log_name:工作線程最后一個提交事務(wù)對應檢查點的主庫 binary log 文件名朴摊。

  • Checkpoint_master_log_pos:工作線程最后一個提交事務(wù)對應檢查點的主庫 binary log 位點。

  • Checkpoint_seqno:工作線程最后一個提交事務(wù)對應 checkpoint_seqno 序號此虑。

  • Checkpoint_group_size:工作線程的 Bitmap 字節(jié)數(shù)甚纲,約等于 GAQ 隊列大小 /8,因為 1 個字節(jié)為 8 位朦前。

  • Checkpoint_group_bitmap:工作線程對應的 Bitmap 位圖信息介杆。

關(guān)于 Checkpoint_group_size 的換算參考函數(shù) Slave_worker::write_info讹弯。

(5)兩個參數(shù)

  • slave_checkpoint_group:GAQ 隊列大小。

  • slave_checkpoint_period:多久執(zhí)行一次檢查點这溅,默認 300 毫秒组民。

(6)檢查點執(zhí)行的時機

  • 超過 slave_checkpoint_period 配置”ィ可參考 next_event 函數(shù)如下:
if (rli->is_parallel_exec() && (opt_mts_checkpoint_period != 0 || force))
{
ulonglong period= static_cast<ulonglong>(opt_mts_checkpoint_period * 1000000ULL);
...
(void) mts_checkpoint_routine(rli, period, force, true/*need_data_lock=true*/);
...
}
  • 達到 GAQ 隊列已滿臭胜,如下:
//如果達到了 GAQ的大小 設(shè)置為force 強制checkpoint
bool force= (rli->checkpoint_seqno > (rli->checkpoint_group - 1));
  • 正常stop slave。

(7)一個列子

通常有壓力的情況下的 slave_worker_info 中的所有工作線程最大的 Checkpoint_master_log_pos 應該和 slave_relay_log_info 中的 Master_log_pos 相等癞尚,因為這是最后一個檢查點的位點信息耸三,如下:

image

三、MTS 中的檢查點的流程

這一部分將詳細描述一下檢查點的步驟浇揩,關(guān)于檢查點可以參考函數(shù) mts_checkpoint_routine仪壮。

假設(shè)現(xiàn)在有 7 個事務(wù)是可以并行執(zhí)行的,工作線程數(shù)量為 4 個胳徽。當前協(xié)調(diào)線程已經(jīng)分發(fā)了 5 個积锅,前面 4 個事務(wù)都已經(jīng)執(zhí)行完成,其中第 5 的一個事務(wù)是大事務(wù)养盗。那么可能當前的狀態(tài)圖如下:

image

前面 4 個事務(wù)每個工作線程都分到一個缚陷,最后一個大事務(wù)這里假設(shè)由工作線程 2 進行執(zhí)行,圖中用紅色部分表示往核。

(1)判斷是超過了 slave_checkpoint_period 設(shè)置的大小箫爷,如果超過需要進行檢查點。

if (!force && diff < period)
//是否需要進行檢查點是否超過了slave_checkpoint_period的設(shè)置
{
/*
We do not need to execute the checkpoint now because
the time elapsed is not enough.
*/
DBUG_RETURN(FALSE);
}

(2)掃描 GAQ 隊列進行出隊操作聂儒,直到第一個沒有提交的事務(wù)為止虎锚。圖中紅色部分就是一個大事務(wù)生兆,檢查點只能停留在它之前屠凶。

cnt= rli->gaq->move_queue_head(&rli->workers);
//work數(shù)組 返回出隊的個數(shù)

move_queue_head 部分代碼如下:

if (ptr_g->worker_id == MTS_WORKER_UNDEF ||
my_atomic_load32(&ptr_g->done) == 0)
//當前GROUP是否已經(jīng)執(zhí)行完成 如果沒有執(zhí)行完成就需要 停止本次檢查點
break; /* 'gap' at i'th */

(3)更新內(nèi)存和 relay_log_info_repository 表的信息為本次檢查點指向的位置。

先更新內(nèi)存信息寒砖,也就是我們 show slave status 中看到的信息:

rli->set_group_master_log_pos(rli->gaq->lwm.group_master_log_pos);
rli->set_group_relay_log_pos(rli->gaq->lwm.group_relay_log_pos);
rli->set_group_relay_log_name(rli->gaq->lwm.group_relay_log_name);

然后強制寫入表 slave\_relay\_log\_info 中:
error= rli->flush_info(TRUE);
//將本次檢查點信息 寫入到relay_log_info_repository表中

(4)更新 last_master_timestamp 信息為檢查點位置事務(wù)的 XID_EVENT 的 timstamp 值谅猾。

這個值在第 27 節(jié)中會詳細描述柄慰,它是計算 Seconds_behind_master 的一個因素:

/*
Update the rli->last_master_timestamp for reporting correct Seconds_behind_master.
If GAQ is empty, set it to zero.
Else, update it with the timestamp of the first job of the Slave_job_queue
which was assigned in the Log_event::get_slave_worker() function.
*/
ts= rli->gaq->empty()? 0 : reinterpret_cast<Slave_job_group*>(rli->gaq->head_queue())->ts;
//rli->gaq->head_queue 檢查點位置的GROUP的時間
rli->reset_notified_checkpoint(cnt, ts, need_data_lock, true);
reset_notified_checkpoint函數(shù)中有:
last_master_timestamp= new_ts;

因此 MTS 中 Seconds_behind_master 的計算和檢查點息息相關(guān)。

(5)最后還會將前面 GAQ 出隊的事務(wù)數(shù)量進行統(tǒng)計税娜,因為每個工作線程需要根據(jù)這個值來進行 Bitmap 位圖的偏移坐搔。并且還會維護我們前面說的 GAQ 的 checkpoint_seqno 值。

這個操作也是在函數(shù) Relay_log_info::reset_notified_checkpoint 中完成的敬矩,實際上很簡單部分代碼如下:

for (Slave_worker **it= workers.begin(); it != workers.end(); ++it)
//循環(huán)每個woker
w->bitmap_shifted= w->bitmap_shifted + shift;
//每個worker線程都會增加 這個偏移量
checkpoint_seqno= checkpoint_seqno - shift;
//這里減去 移動的個數(shù)

到這里整個檢查點的基本操作就完成了概行。我們看到實際上步驟并不多,拿到 Bitmap 偏移量后每個工作線程就會在隨后的第一個事務(wù)提交的時候進行位圖的偏移弧岳,checkpoint_seqno 計數(shù)也會更新凳忙。

我們前面的假設(shè)環(huán)境中业踏,如果觸發(fā)了一次檢查點,并且協(xié)調(diào)線程將后兩個可以并行的事務(wù)發(fā)給了工作線程 1 和 3 進行處理并且處理完成涧卵。那么我們的圖會變成如下:

image

這張圖中我用不同樣色表示了不同線條勤家,因為它們交叉比較多。GAQ 中的紅色事務(wù)就是我們假設(shè)的大事務(wù)它仍然沒有執(zhí)行完成柳恐,它也是我們所謂的 ‘gap’伐脖。如果這個時候 MySQL 實例異常重啟,那么這個紅色 ‘gap’ 就是我們啟動后需要找到的事務(wù)乐设,方式就是通過 Bitmap 位圖進行比對讼庇,后面說異常恢復的時候再詳細討論近尚。如果是開啟了 GTID蠕啄,這種 ‘gap’ 很容易就能觀察到,下一節(jié)將進行測試戈锻。

同時我們需要注意這個時候工作線程 2 并沒有分發(fā)新的事務(wù)執(zhí)行歼跟,因為工作線程 2 沒有執(zhí)行完大事務(wù), 因此在 slave_woker_info 表中它的信息仍然顯示為上一次提交事務(wù)的信息舶沛。而工作線程 4 因為沒有分配到新的事務(wù)嘹承,因此 slave_woker_info 表中它的信息也顯示為上一次提交事務(wù)的信息。因此在 slave_woker_info 中工作線程 2 和工作線程 4 的檢查點信息如庭、Bitmap 信息、checkpoint_seqno 都是老的信息撼港。

總結(jié)

好了坪它,到這里我已經(jīng)說明了 MTS 中三個關(guān)鍵點。

  • 協(xié)調(diào)線程是根據(jù)什么規(guī)則進行事務(wù)分發(fā)的帝牡。

  • 工作線程如何拿到分發(fā)的事務(wù)往毡。

  • MTS 中的檢查點是如何進行的。

但是還有一個關(guān)鍵點沒有說靶溜,就是前面多次提到的異晨t;謴停?25 節(jié)將重點解釋罩息。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嗤详,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子瓷炮,更是在濱河造成了極大的恐慌葱色,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件娘香,死亡現(xiàn)場離奇詭異苍狰,居然都是意外死亡办龄,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進店門淋昭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來俐填,“玉大人,你說我怎么就攤上這事翔忽$枨荩” “怎么了?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵呀打,是天一觀的道長矢赁。 經(jīng)常有香客問我,道長贬丛,這世上最難降的妖魔是什么撩银? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮豺憔,結(jié)果婚禮上额获,老公的妹妹穿的比我還像新娘。我一直安慰自己恭应,他們只是感情好抄邀,可當我...
    茶點故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著昼榛,像睡著了一般境肾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上胆屿,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天奥喻,我揣著相機與錄音,去河邊找鬼非迹。 笑死环鲤,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的憎兽。 我是一名探鬼主播冷离,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼纯命!你這毒婦竟也來了西剥?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤扎附,失蹤者是張志新(化名)和其女友劉穎蔫耽,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡匙铡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年图甜,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鳖眼。...
    茶點故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡黑毅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出钦讳,到底是詐尸還是另有隱情矿瘦,我是刑警寧澤,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布愿卒,位于F島的核電站缚去,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏琼开。R本人自食惡果不足惜易结,卻給世界環(huán)境...
    茶點故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望柜候。 院中可真熱鬧搞动,春花似錦、人聲如沸渣刷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辅柴。三九已至箩溃,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間碌识,已是汗流浹背碾篡。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留筏餐,地道東北人。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓牡拇,卻偏偏與公主長得像魁瞪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子惠呼,可洞房花燭夜當晚...
    茶點故事閱讀 43,514評論 2 348

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