Redis面試之常見問題

轉載于:https://mp.weixin.qq.com/s/qvXm1pU8T_2mCZCjkTR7QA

1 Redis常見面試問題

1.1 Redis是單線程還是多線程

Redis不同版本之間采用的線程模型是不一樣的,在Redis4.0版本之前使用的是單線程模型,在4.0版本之后增加了多線程的支持。

4.0之前雖然說Redis是單線程,也只是說它的網絡I/O線程以及SetGet操作是由一個線程完成的。但是Redis持久化、集群同步還是使用其他線程來完成。

4.0之后引入了多線程的支持,主要是體現在大數據的異步刪除功能上,例如 unlink key、flushdb async、flushall async

隨著底層網絡硬件性能的提升,Redis 的性能瓶頸逐漸體現在網絡 I/O 的讀/寫上,單個線程處理網絡讀/寫的速度跟不上底層網絡硬件執行的速度。讀/寫網絡的讀/寫系統調用占用了 Redis 執行期間大部分 CPU 時間,所以 Redis 采用多個 I/O 線程來處理網絡請求,提高網絡請求處理的并行度
不過Redis6.0 版本開始正式宣布支持多線程模型,需要注意的是,RedisI/O 線程模型只用來處理網絡讀/寫請求Redis讀/寫命令依然是單線程處理的。

1.2 使用單線程原因

那為什么Redis在4.0之前會選擇使用單線程?而且使用單線程還那么快?

選擇單線程主要是使用簡單,不存在鎖競爭,可以在無鎖的情況下完成所有操作,不存在死鎖和線程切換帶來的性能和時間上的開銷,但同時單線程也不能完全發揮出多核CPU性能

為什么單線程那么快主要有以下幾個原因:

  • Redis的大部分操作都在內存中完成,內存中的執行效率本身就很快,并且采用了高效的數據結構,比如哈希表和跳表。
  • 使用單線程避免了多線程的競爭,省去了多線程切換帶來的時間和性能開銷,并且不會出現死鎖。
  • 采用I/O 多路復用機制處理大量客戶端的Socket請求,因為這是基于非阻塞的 I/O 模型,這就讓Redis可以高效地進行網絡通信,I/O的讀寫流程也不再阻塞。
  • 很多的客戶端連接先到linux中的內核,內核和redis中間使用的是epoll(非阻塞的多路復用),那些進程一筆一筆的進行的。

在分布式情況下,這個數據一致性很重要。每個連接里邊命令是順序到達、順序處理的,但是如果說里面有個key,這個key為a,那么兩個客戶端發了一個對a的操作,那么無論從網絡當中跳躍誰先到達的,或者指定誰先輪到誰了。那么其實這兩個人對一個的操作,很難判定是誰先誰后,但是如果是你一個人,它里邊線性,而且沒有使用多線程,線程還是安全的,雖然它可以有多線程,但是線程安全,對a的操作,這邊能控制住,先創建a再刪除a,只要這邊能操作的話,那么這個數據是可以保證的。如果是單線程,這個客戶端就是一個線程,就是一個socket里面也是一個線程,那么這個線程肯定是先發出一個創建a再發出一個刪除a,但是如果客戶端里邊是多線程,那么這里一個創建命令和刪除命令,指不定誰跑到前面了,如果線程不是安全的話,那么有可能先把刪除的發出去,再把創建的發出去。

請添加圖片描述

點擊了解Linux中epoll原理機制

1.3 Redis高可用

Redis實現高可用主要有三種方式:主從復制、哨兵模式,以及 Redis 集群

1.3.1 主從復制

將從前的一臺 Redis 服務器,同步數據到多臺從 Redis 服務器上,即一主多從的模式,這個跟MySQL主從復制的原理一樣。

在這里插入圖片描述

點擊了解redis 持久化中的主從復制同步

1.3.2 哨兵模式

1.3.2.1 簡介

使用 Redis 主從復制的時候,會有一個問題,就是當 Redis 的主從服務器出現故障宕機時,需要手動進行恢復,為了解決這個問題,Redis 增加了哨兵模式(因為哨兵模式做到了可以監控主從服務器,并且提供自動容災恢復的功能)。
它專注于對 Redis 實例(主節點、從節點)運行狀態的監控,并能夠在主節點發生故障時通過一系列的機制實現選主主從切換,實現自動故障轉移,確保整個 Redis 系統的可用性。

image.png

sentinel 主要做四件事情:

  • 監控 masterslave 狀態,判斷是否下線。

    • 每秒一次的頻率向 masterslave 以及其他 sentinel 發送 PING 命令,如果該節點距離最后一次響應 PING 的時間超過 down-after-milliseconds 選項所指定的值(即在設定的時間內沒有回復心跳包), 則這個實例會被 Sentinel 標記為主觀下線,當 master 被標記主觀下線。
    • 其他正在監視這個 master 的所有 sentinel 會按照每秒一次的頻率確認 master 是否主觀下線。
    • 當超過配置的 quorum 數量的 sentinel 都認為 master 主觀下線,則標記這個 master 客觀下線。
  • 選舉新 master,如果 master 出現故障,sentine 需要選舉一個 slave 晉升為新 master。晉升為新 master 的 slave 是有條件的,先過濾不滿足條件的,再打分排優先級。

    • 過濾掉下線、網絡異常的 slave。
    • 過濾掉經常與 master 斷開的 slave。
    • slave 優先級,通過 replica-priority 100 配置,值越高,優先級越高。
    • 若優先級相同,則復制偏移量(processed replication offset)最大的從節點,已復制的數據量越多越好,slave_repl_offsetmaster_repl_offset 差值越小。
    • slave runID,在優先級和復制進度都相同的情況下,runID 最小的 slave 得分最高,會被選為新主庫。
  • 選舉領導者哨兵(Leader Sentinel),執行主從切換,從 sentinel 集群中選舉一個 leader 執行故障自動切換。
    成為 leader 的條件是收到足夠的贊成票(大于等于quorum和(總節點數/2 + 1)的最大值)。
    第一個判定 master 主觀下線的 sentinel 收到其他 sentinel 節點的回復并確定 master 客觀下線后,就會給其他 sentinel 節點發送命令申請成為 leader。
    選舉領導者哨兵而不是直接從從節點中選舉新的主節點,主要是為了以下原因:

    • 協調一致性:通過選舉領導者哨兵,可以確保在集群中僅有一個哨兵負責主從切換,避免多個哨兵同時進行切換操作導致的不一致性。
    • 集中決策:領導者哨兵集中管理整個主從切換過程,使得切換過程更加有序和可控。
    • 分擔職責:哨兵負責監控和管理 Redis 節點,而從節點主要用于數據復制和故障切換。在發生故障時,選舉領導者哨兵來管理切換過程,可以讓從節點專注于數據同步,分擔系統負載
  • 通知,通知其他 slave 執行 replicaof 與新的 master 同步數據,并通知客戶端與新 master 建立連接。

    image.png

  • 哨兵如何通知客戶端?
    選出新主節點后,哨兵的任務還沒完。它需要告訴所有從節點客戶端:主節點已經更換
    對于從節點,哨兵會自動修改它們的復制目標,對于客戶端,哨兵會通過發布訂閱通知它們新的主節點地址。
    哨兵會通過 Redis發布訂閱系統,把新主節點的信息推送給所有訂閱的客戶端。
    例如,使用 SENTINEL get-master-addr-by-name mymaster 命令可以獲取新主節點的 IP 和端口。

redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster
# 返回新主節點的 IP 和端口,比如 [ "127.0.0.1", "6380" ]

1.3.2.2 哨兵機制的高可用性保障

一個哨兵單點可能會掛掉,所以 Redis 支持多哨兵協同工作。這種模式下,哨兵之間也會相互通信,通過 Raft 類似的選舉機制選出一個領頭哨兵Leader Sentinel),由它負責協調故障轉移。

每個哨兵實例都可以彼此通信,確保在任何一個哨兵掛掉的情況下,系統仍然能正常工作。

哨兵機制是 Redis 提供的高可用解決方案,主要功能包括監控主從節點狀態、進行故障轉移以及通知客戶端更新主節點信息。

  • 監控:哨兵通過 PING 命令定期檢測主從節點是否存活,基于響應時間判斷節點狀態。
  • 選主:哨兵根據復制偏移量、優先級和網絡延遲等標準選舉新的主節點,保證數據一致性和快速恢復。
  • 高可用性:通過多個哨兵協同工作,避免單點故障。
  • 通知:哨兵利用發布訂閱機制通知客戶端主節點變更,確保業務無縫切換。

1.3.3 Redis Cluster(集群)

哨兵模式 基于主從模式,實現讀寫分離,它還可以自動切換,系統可用性更高。但是它每個節點存儲的數據是一樣的,浪費內存,并且不好在線擴容。因此,Reids Cluster集群(切片集群的實現方案)應運而生,它在Redis3.0加入的,實現了Redis的分布式存儲。對數據進行分片,也就是說每臺Redis節點上存儲不同的內容,來解決在線擴容的問題。并且,它可以保存大量數據,即分散數據到各個Redis實例,還提供復制和故障轉移的功能。

Redis Cluster 是一種分布式去中心化的運行模式,是在 Redis 3.0 版本中推出的 Redis 集群方案,通過分片(sharding)來進行數據管理(分治思想的一種實踐),并提供復制和故障轉移功能,它將數據分布在不同的服務器上,以此來降低系統對單主節點的依賴,從而提高 Redis 服務的讀寫性能。

圖片

使用哨兵模式在數據上有副本數據做保證,在可用性上又有哨兵監控,一旦master宕機會選舉salve節點為master節點,那為什么還需要使用集群模式呢?
哨兵模式歸根節點還是主從模式,在主從模式下我們可以通過增加salve節點來擴展讀并發能力,但是沒辦法擴展寫能力和存儲能力,存儲能力只能是master節點能夠承載的上限。所以為了擴展寫能力和存儲能力,我們就需要引入集群模式。

集群中那么多Master節點,redis cluster在存儲的時候如何確定選擇哪個節點呢?
Redis Cluster采用的是數據分片實現節點選擇的

Redis集群搭建

1.4 Redis內存(數據)淘汰策略

redis中,我們是可以去設置最大使用內存大小server.maxmemory的,當redis內存數據集大小上升到一定程度的時候,就會施行數據淘汰機制。
不同位數的操作系統,maxmemory 的默認值是不同的:

  • 在 64 位操作系統中,maxmemory 的默認值是 0,表示沒有內存大小限制,那么不管用戶存放多少數據到 Redis 中,Redis 也不會對可用內存進行檢查,直到 Redis 實例因內存不足而崩潰也無作為。
  • 在 32 位操作系統中,maxmemory 的默認值是 3G,因為 32 位的機器最大只支持 4GB 的內存,而系統本身就需要一定的內存資源來支持運行,所以 32 位操作系統限制最大 3 GB 的可用內存是非常合理的,這樣可以避免因為內存不足而導致 Redis 實例崩潰。

Redis提供了8種數據淘汰策略,分為不進行數據淘汰進行數據淘汰兩類策略,
不進行數據淘汰的策略 noevictionRedis3.0之后,默認的內存淘汰策略),它表示當運行內存超過最大設置內存時,不淘汰任何數據,這時如果有新的數據寫入,會報錯通知禁止寫入,不淘汰任何數據,但是如果沒用數據寫入的話,只是單純的查詢或者刪除操作的話,還是可以正常工作。

  • no-enviction:禁止淘汰數據,如果redis寫滿了將不提供寫請求,直接返回錯誤

進行數據淘汰的策略:

  • volatile-lru:從已經設置過期時間的數據集中,挑選最近最少使用的數據淘汰,即:最久未使用的鍵值
  • volatile-ttl:從已經設置過期時間的數據集中,挑選即將要過期的數據淘汰。
  • volatile-random:從已經設置過期時間的數據集中,隨機挑選數據淘汰。
  • volatile-lfu:從已經設置過期時間的數據集中,會使用LFU算法選擇設置了過期時間的鍵值對,即:最不常用的鍵值
  • allkeys-lru:從所有的數據集中,挑選最近最少使用的數據淘汰。
  • allkeys-random:從所有的數據集中,隨機挑選數據淘汰。
  • allkeys-lfu:淘汰整個鍵值中最不常用的鍵值

附錄:LRULFU是不同的:

  • LRU是最近最少使用頁面置換算法(Least Recently Used),也就是首先淘汰最長時間未被使用的頁面
  • LFU是最近最不常用頁面置換算法(Least Frequently Used),也就是淘汰一定時期內被訪問次數最少的頁

使用策略規則:

  • 如果數據呈現冪律分布,也就是一部分數據訪問頻率高,一部分數據訪問頻率低,則使用allkeys-lru
  • 如果數據呈現平等分布,也就是所有的數據訪問頻率都相同,則使用allkeys-random

1.5 Redis過期鍵刪除策略

Redis過期鍵刪除策略:

  • 定時刪除:在設置鍵的過期時間的同時,創建一個timer,讓定時器在鍵的過期時間到達時,立即執行對鍵的刪除操作。(主動刪除)
    對內存友好,但是對cpu時間不友好,有較多過期鍵的而情況下,刪除過期鍵會占用相當一部分cpu時間。
  • 惰性刪除:放任過期鍵不管,但是每次從鍵空間中獲取鍵時,都檢查取到的鍵是否過去,如果過期就刪除,如果沒過期就返回該鍵。(被動刪除)
    cpu時間友好,程序只會在取出鍵的時候才會對鍵進行過期檢查,這不會在刪除其他無關過期鍵上花費任何cpu時間,但是如果一個鍵已經過期,而這個鍵又保留在數據庫中,那么只要這個過期鍵不被刪除,他所占用的內存就不會釋放,對內存不友好。
    這種方式在Redis中主要是通過expireIfNeeded函數來實現的
    Redis 4.0之后引入的,叫做lazyfree_lazy_expire,它決定了是否異步刪除過期鍵。如果設置為1,表示使用異步刪除,避免阻塞其他操作;如果設置為0,則會同步刪除,刪除操作會阻塞當前請求,直到刪除完成
  • 定期刪除:每隔一段時間就對數據庫進行一次檢查,刪除里面的過期鍵。(主動刪除)采用對內存cpu時間折中的方法,每隔一段時間就對一些 key 進行采樣檢查,檢查是否過期,如果過期就進行刪除
    定期刪除的檢查周期默認是每秒執行10次,可以通過hz配置來調整。默認情況下,hz值為10。
    定期刪除的邏輯是這樣的:
    • 采樣一定個數的key默認20),采樣的個數可以進行配置,并將其中過期的 key 全部刪除;
    • 如果過期 key 的占比超過可接受的過期 key 的百分比(默認25%),則重復刪除的過程,直到過期key的比例降至可接受的過期 key 的百分比以下
    • 注意:為了防止定期刪除過程中的循環過長,Redis限制了每輪檢查的最大時間,默認情況下不會超過25毫秒。如果在25毫秒內沒有完成過期鍵的刪除,Redis會強制退出當前輪次,等到下一輪繼續處理。

1.6 Redis的key和value可以存儲的最大值分別是多少

雖然Key的大小上限為512M,但是一般建議key的大小不要超過1KB,這樣既可以節約存儲空間,又有利于Redis進行檢索。
value的最大值也是512M。對于String類型的value值上限為512M,而集合、鏈表、哈希等key類型,單個元素的value上限也為512M

1.7 Redis實現數據的去重

  • Redisset:它可以去除重復元素,也可以快速判斷某一個元素是否存在于集合中,如果元素很多(比如上億的計數),占用內存很大。
  • Redisbit:它可以用來實現比set內存高度壓縮的計數,它通過一個bit設置為1或者0,表示存儲某個元素是否存在信息。例如網站唯一訪客計數,可以把user_id作為 bit的偏移量 offset,如設置為1表示有訪問,使用1 MB的空間就可以存放800多萬用戶的一天訪問計數情況。
  • HyperLogLog:實現超大數據量精確的唯一計數都是比較困難的,HyperLogLog可以僅僅使用 12 k左右的內存,實現上億的唯一計數,而且誤差控制在百分之一左右。
  • bloomfilter布隆過濾器:布隆過濾器是一種占用空間很小的數據結構,它由一個很長的二進制向量和一組Hash映射函數組成,它用于檢索一個元素是否在一個集合中

1.8 Redis序列化

Redis什么時候需要序列化?

  • 序列化:將 Java 對象轉換成字節流的過程。
  • 反序列化:將字節流轉換成 Java 對象的過程。
image.png

為什么需要序列化呢?
打個比喻:作為大城市漂泊的碼農,搬家是常態。當我們搬書桌時,桌子太大了就通不過比較小的門,因此我們需要把它拆開再搬過去,這個拆桌子的過程就是序列化。而我們把書桌復原回來(安裝)的過程就是反序列化啦。

比如想把內存中的對象狀態保存到一個文件中或者數據庫中的時候(最常用,如保存到redis);再比喻想用套接字在網絡上傳送對象的時候,都需要序列化。
RedisSerializer接口 是 Redis 序列化接口,用于 Redis KEYVALUE 的序列化,有如下序列化方式:

  • JDK 序列化方式 (默認)
  • String 序列化方式
  • JSON 序列化方式
  • XML 序列化方式

1.9 大key

1.9.1 定義

Redis 中的 大key 是指存儲在Redis中的占用內存較大的鍵值對。大key可能會導致Redis的性能下降,因為大key占用的內存較多,需要較長的時間來進行讀寫操作。而且,當大key被刪除時,會阻塞Redis的其他操作。

常見的大key包括:

  • 存儲大量數據的字符串類型鍵值對。
  • 存儲大量元素的列表、集合或有序集合。
  • 包含大量字段的哈希表。

1.9.2 大Key解決方案

為了避免大keyRedis性能的影響,可以采取以下措施:

  • 大key拆分為多個較小的鍵值對,以減少每個鍵值對的內存占用。
  • 使用分布式緩存,將大key分散到多個Redis實例上。
  • 使用壓縮算法對大key進行壓縮,減少內存占用。
  • 使用Redis的分片功能,將大key分散到多個分片上,減少單個Redis實例的負載。
  • 大Key進行清理。將不適用Redis能力的數據存至其它存儲,并在Redis中刪除此類數據。注意,要使用異步刪除。
  • 監控Redis的內存水位。可以通過監控系統設置合理的Redis內存報警閾值進行提醒,例如Redis內存使用率超過70%、Redis的內存在1小時內增長率超過20%等。
  • 對過期數據進行定期清。堆積大量過期數據會造成大Key的產生,例如在HASH數據類型中以增量的形式不斷寫入大量數據而忽略了數據的時效性??梢酝ㄟ^定時任務的方式對失效數據進行清理。

1.10 熱Key

1.10.1 定義

Redis熱key是指在Redis中被頻繁訪問的鍵。當某個鍵被頻繁訪問時,它就被認為是熱key。熱key通常是由于某些熱門操作、熱門數據或者高并發訪問所導致的

通常以其接收到的 Key 被請求頻率來判定,例如:

  • QPS集中在特定的Key:Redis實例的總QPS(每秒查詢率)為10,000,而其中一個Key的每秒訪問量達到了7,000。
  • 帶寬使用率集中在特定的Key:對一個擁有上千個成員且總大小為1 MB的HASH Key每秒發送大量的HGETALL操作請求。
  • CPU使用時間占比集中在特定的Key:對一個擁有數萬個成員的Key(ZSET類型)每秒發送大量的ZRANGE操作請求。

1.10.2 如何解決熱key

熱key可能對Redis的性能產生重大影響。當一個鍵被頻繁訪問時,Redis需要頻繁地從內存中讀取或寫入該鍵的值,這可能導致內存帶寬的瓶頸、CPU利用率的增加以及延遲的增加。此外,如果一個熱key的值過大,可能會占用大量的內存,進一步影響Redis的性能
解決方案:

  • Redis集群架構中對熱Key進行復制,數據分片
    Redis集群架構中,由于熱Key的遷移粒度問題,無法將請求分散至其他數據分片,導致單個數據分片的壓力無法下降。此時,可以將對應熱Key進行復制并遷移至其他數據分片,例如將熱Key foo復制出3個內容完全一樣的Key并名為foo2、foo3、foo4,將這三個Key遷移到其他數據分片來解決單個數據分片的熱Key壓力。
  • 使用讀寫分離架構。
    如果熱Key的產生來自于讀請求,您可以將實例改造成讀寫分離架構來降低每個數據分片的讀請求壓力,甚至可以不斷地增加從節點。但是讀寫分離架構在增加業務代碼復雜度的同時,也會增加Redis集群架構復雜度。不僅要為多個從節點提供轉發層(如Proxy,LVS等)來實現負載均衡,還要考慮從節點數量顯著增加后帶來故障率增加的問題。Redis集群架構變更會為監控、運維、故障處理帶來了更大的挑戰。
  • 使用合適的數據結構
    根據實際情況選擇合適的數據結構,如列表、集合、有序集合等,來存儲熱key的值,以提高讀寫性能。
  • 緩存策略
    使用合理的緩存策略,比如設置合適的過期時間、使用LRU算法等,以減少對熱key的訪問頻率。
  • 持久化策略
    根據業務需求選擇合適的持久化方式,如RDB快照、AOF日志等,以確保數據的安全性和可靠性。

1.11 Redis中緩沖區

Redis中的緩沖區主要根據其功能和用途進行劃分,可以歸納為以下幾種:

  • 客戶端緩沖區:
    • 輸入緩沖區:緩存客戶端發送過來的命令,Redis主線程從該緩沖區中讀取命令進行處理。
    • 輸出緩沖區:當 Redis主線程處理完數據后,將結果寫入該緩沖區,再返回給客戶端。
    • 緩存區溢出原因:
      • 寫入了BigKey,即一次性寫入了大量數據,超過了緩沖區的大小。
      • 服務端處理請求的速度過慢,導致無法及時處理請求,使得客戶端發送的請求在緩沖區內越積越多。
  • 復制緩沖區:
    • 復制緩沖區:在全量復制過程中,主節點在向從節點傳輸RDB文件的同時,會繼續接收客戶端發送的寫命令請求。這些寫命令會先保存在復制緩沖區中,等RDB文件傳輸完成后,再發送給從節點去執行。
    • 復制積壓緩沖區:在增量復制時,主節點和從節點進行常規同步時,會把寫命令暫存在復制積壓緩沖區中。
    • 溢出原因:
      • 主庫傳輸RDB文件以及從庫加載RDB文件耗時長,同時主庫接收的寫命令操作較多。
      • 緩沖區大小設置不合理。
  • AOF緩沖區:
    • AOF緩沖區:當Redis進行持久化時,會先將客戶端傳來的命令存放在AOF緩沖區,再根據具體的策略(always、everysec、no)去寫入磁盤中的AOF文件中。
    • AOF重寫緩沖區:在Redis進行AOF重寫時,主進程會fork一個子進程進行AOF重寫,此時主進程接收的指令會存放在AOF重寫緩沖區中。當AOF重寫完成后,這些指令會被追加到AOF文件中。
  • 內存級緩存:
    雖然不是嚴格意義上的緩沖區,但Redis的內存級緩存是其最常見的使用場景。通過將數據存儲在內存中,減少讀取數據庫的頻率,提高數據訪問速度。
  • 其他內存使用:
    • Redis空進程自身內存消耗非常少,通常used_memory_rss在3MB左右,used_memory在800KB左右。
    • 對象內存:存儲著用戶所有的數據,消耗可以簡單理解為sizeof(keys)+sizeof(values)。
    • 內存碎片:正常的碎片率在1.03左右
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容