Redis學(xué)習(xí)之旅~基礎(chǔ)應(yīng)用篇

這一年以來,寫了太多的業(yè)務(wù)代碼漫试。是時候要總結(jié)一下自己的積累了贮缅。本文是redis深度歷險的讀書筆記凭豪,做個記錄以及分享給大家汁蝶。

docker redis redis插件加載

數(shù)據(jù)結(jié)構(gòu)

字符串

  • 字符串是一個字符數(shù)組

  • 常見用途就是信息JSON序列化成為字符串之后,存入redis窃躲,取信息會經(jīng)過一次反序列化

  • 字符串是動態(tài)字符串计贰,可以進行擴容;當(dāng)字符串長度小于1m的時候蒂窒,擴容是加倍躁倒。當(dāng)大于1m,每次擴容就增加1m洒琢。字符串最大的長度為512m

  • 使用字符串計數(shù)的時候秧秉,單個值的最大值為signed long,超過就會報錯

  • 常用命令

    set name a
    
    get name
    
    exists a
    
    del a
    
    mset a 1 b 2
    
    mget a b
    
    expire a 5
    
    setex a 5 1
    
    setnx a 1
    
    incr a
    
    incrby a 10
    

列表

  • 相當(dāng)于java中的LinkedList,一個雙向鏈表。插入和刪除操作快器腋,但是索引定位很慢钞诡。

    
    這里幫助大家回憶一下普通節(jié)點刪除操作
    
    p.pre.next = p.next
    
    p.next.pre = p.pre
    
    p = null
    
    不需要對節(jié)點進行移動
    
  • 列表可以作為隊列板祝,右進左出即可

    rpush books java python golang kotlin
    
    4
    
    llen books
    
    4
    
    lpop books
    
    java
    
    
  • 列表可以作為棧,右進右出即可。就是,調(diào)整了隊列使用的方向

    rpush books java python golang kotlin
    4
    llen books
    4
    rpop books
    kotlin
    
  • 使用隊列會存在的慢操作有g(shù)et(index)方法汪厨,需要遍歷整個鏈表,性能為O(n)

    rpush books java python golang kotlin
    4
    lindex 1            # 時間為O(n)
    lindex books 1
    "python"
    lrange books 0 -1
    "java"
    "python"
    "golang"
    "kotlin"
    ltrim books 1 -1      # 設(shè)置長度為3的數(shù)組愉择,區(qū)間外的全部被清除
    OK
    lrange books 0 -1    # 獲取所有元素
    "python"
    "golang"
    "kotlin"
    llen books
    3
    ltrim books 1 0        # 刪除所有元素
    OK
    
    
  • 快速列表:在列表元素較少的時候劫乱,會使用一塊連續(xù)的內(nèi)存,叫ziplist(壓縮列表)锥涕。當(dāng)數(shù)據(jù)量較多的時候衷戈,就要變成quicklist(快速列表),多個ziplist使用雙向指針串起來使用。


// 快速列表

struct quicklist {

  quicklistNode* head;

  quicklistNode* tail;

  long count; // 元素總數(shù)

  int nodes; // ziplist 節(jié)點的個數(shù)

  int compressDepth; // LZF 算法壓縮深度

  ...

}

// 快速列表節(jié)點

struct quicklistNode {

  quicklistNode* prev;

  quicklistNode* next;

  ziplist* zl; // 指向壓縮列表

  int32 size; // ziplist 的字節(jié)總數(shù)

  int16 count; // ziplist 中的元素數(shù)量

  int2 encoding; // 存儲形式 2bit层坠,原生字節(jié)數(shù)組還是 LZF 壓縮存儲

}

struct ziplist_compressed {

  int32 size;

  byte[] compressed_data;

}

struct ziplist {

  ...

}

Hash字典

  • 內(nèi)部實現(xiàn)結(jié)構(gòu)就是數(shù)組+鏈表的方式進行實現(xiàn)殖妇。相同hash值的元素使用鏈表串聯(lián)起來。java的鏈表數(shù)量超過6個會變成紅黑樹窿春,不知道redis的會不會有這個查詢優(yōu)化拉一。

  • redis里面的值就只能是字符串,便于比較旧乞。不像java的hashmap可以存儲對象蔚润,equals方法的編寫可謂五花八門

  • java的hashmap在進行rehash的時候,需要一次性進行rehash尺栖,是個耗時操作嫡纠,多線程還會出現(xiàn)環(huán)。redis的是漸進式rehash延赌。rehash的時候除盏,會保留兩個hash結(jié)構(gòu),查詢時候會查詢兩個hash結(jié)構(gòu)挫以,在后續(xù)的定時任務(wù)和hash指令操作的時候者蠕,漸進地將舊的內(nèi)容遷移到新的hash結(jié)構(gòu)中。當(dāng)內(nèi)容遷移完畢掐松,刪除舊的hash結(jié)構(gòu)

    hset books java "think in java"
    1
    hset books golang "concurrency in go"
    1
    hset books python "python cookbook"
    1
    hgetall books  # 返回所有entryies踱侣,key和value間隔返回
    "java"
    "think in java"
    "golang"
    "concurrency in go"
    "python"
    "python cookbook"
     hlen books
     3
    hget books java
    "think in java"
    hset books java "my java book"
    0
    hmset books java "java" golang "golang"
    ok
    hset user-lixinkun age 25
    1
    hincrby user-lixinkun 1
    26
    

set

  • 相當(dāng)于java的hashset,確保唯一性

    sadd books java
    1
    sadd books java
    0
    sadd books python golang
    smembers books
    java
    golang
    python    #順序可能被打亂,輸出結(jié)果是無序的
    

Zset 有序列表

  • 類似于java的sortedset和hashmap的結(jié)合體大磺。具備set的唯一性原理抡句,也可以給每個值賦一個score,代表它的排序權(quán)重杠愧。它的內(nèi)部實現(xiàn)是一個跳躍列表待榔。貌似java也有這個數(shù)據(jù)結(jié)構(gòu)

  • 內(nèi)部排序通過skiplist實現(xiàn)。zset要支持睡覺的插入和刪除流济,不能使用數(shù)組來表示锐锣。普通的鏈表數(shù)據(jù)結(jié)構(gòu),有新元素插入的時候袭灯,需要進行定位刺下,這樣才能保證是有序的。通常會通過二分查找來確定位置稽荧,但是只有數(shù)組才支持快速查找橘茉。這時候就需要一個多層級的列表結(jié)構(gòu),最下面一層的元素都是串聯(lián)起來的姨丈。某個元素存在于所有的層畅卓。定位插入點的時候,先在頂層進行定位蟋恬,然后下潛到下一層翁潘,然后一直到最底下一層。redia采用隨機策略來決定元素可以兼職到第幾層歼争。L0層的概率是100%拜马,L1的是50%渗勘。這樣去類推

  • 數(shù)據(jù)結(jié)構(gòu),具體介紹鏈接redis跳躍列表介紹

    /* ZSETs use a specialized version of Skiplists */
    typedef struct zskiplistNode {
    //成員對象
    robj *obj;
    //分值
    double score;
    //后退指針
    struct zskiplistNode *backward;
    //層
      struct zskiplistLevel {
          struct zskiplistNode *forward;//前進指針
          unsigned int span;//跨度
      } level[];
    } zskiplistNode;
    typedef struct zskiplist {
    //表頭節(jié)點和表尾節(jié)點
    struct zskiplistNode *header, *tail;
    //表中節(jié)點的的數(shù)量
    unsigned long length;
    //表中層數(shù)最大的節(jié)點層數(shù)
    int level;
    } zskiplist;
    

容器的通用規(guī)則

  • create if not exists.如果容器不存在,則先創(chuàng)建容器俩莽。

  • drop if no elements.如果容器沒有元素了旺坠,就立即刪除該容器,釋放內(nèi)存扮超。

  • 容器的過期時間是針對整個對象的取刃,hash的過期是整個進行過期,而不是單個元素

分布式鎖

  • 對于不是原子性的操作出刷,就需要通過分布式鎖來保證只有一個線程可以獲取到鎖璧疗。具體使用的是setnx和del命令。鎖處理完了以后馁龟,就要被釋放崩侠。中間需要解決的問題就是,如果業(yè)務(wù)邏輯執(zhí)行之間坷檩,出現(xiàn)了異常啦膜,可能導(dǎo)致del無法執(zhí)行,整個業(yè)務(wù)癱瘓淌喻。

  • 或許僧家,可以加一個過期時間,5s以后自動釋放裸删。但是邏輯還有問題的八拱,如果expire的時候,命令丟失了涯塔,也會死鎖肌稻。出現(xiàn)這種問題的原因就是setnx和expire兩個命令不是原子性的。

  • 在redis2.8的版本中匕荸,作者加入了set指令的擴展餐廚爹谭,setnx和expire指令可以一起執(zhí)行了,指令是原子性的榛搔。

加鎖
set lock:book true ex 5 nx
//釋放鎖
del lock:book
  • 超時問題是redis分布式鎖不能解決的問題诺凡,如果加鎖和釋放鎖之間的邏輯太長,超出了鎖的超時時限践惑,那么第二個線程可能獲得redis分布式鎖腹泌。所以,redis分布式鎖不要用于較長時間的任務(wù)

  • 可重入性尔觉。java里面有一個ReentrantLock凉袱。Redis分布式鎖如果要支持重入,需要對客戶端的set方法進行封裝,使用線程的ThreadLocal遍歷存儲當(dāng)前持有鎖的計數(shù)专甩。

  • 下面代碼redis分布式鎖的使用钟鸵,這里鏈接是錯誤案例介紹redis分布式鎖使用與錯誤示例


//錯誤使用就是使用setnx和expire兩個指令

/**

* 嘗試獲取分布式鎖
* @param jedis Redis客戶端
* @param lockKey 鎖
* @param requestId 請求標(biāo)識
* @param expireTime 超期時間
* @return 是否獲取成功
*/
public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
    String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
    if (LOCK_SUCCESS.equals(result)) {
        return true;
    }
    return false;
}
  • java版本的可重入鎖,核心就是用threadlocal去記錄每個線程,某個key的鎖計數(shù)
public class RedisWithReentrantLock{
  //線程鎖計數(shù)器8
  private ThreadLocal<Map<String,Integer>> lockers = new ThreadLocal<>();

  private Jedis jedis;

  //內(nèi)部使用的lock
  private boolean _lock(String key) {
    return jedis.set(key, "", "nx", "ex", 5L) != null;
  }

  //內(nèi)部使用的unlock
  private void _unlock(String key) {
    jedis.del(key);
  }

  //獲取計數(shù)器
  private Map<String, Integer> currentLockers() {
    Map<String, Integer> refs = lockers.get();
    if (refs != null) {
      return refs;
    }
    lockers.set(new HashMap<>());
    return lockers.get();
  }
  //可重入加鎖
  public boolean lock(String key) {
    Map<String, Integer> refs = currentLockers();
    Integer refCnt = refs.get(key);
    if (refCnt != null) {
      refs.put(key, refCnt + 1);
      return true;
    }
    boolean ok = _lock(key);
    if (!ok) {
      return false;
    }
    refs.put(key, 1);
    return true;
  }
  //可重入釋放鎖
  public boolean unlock(String key) {
    Map<String, Integer> refs = currentLockers();
    Integer refCnt = refs.get(key);
    if (refCnt == null) {
      return false;
    }
    refCnt = -1;
    if (refCnt > 0) {
      refs.put(key, refCnt);
    } else {
      refs.remove(key);
      _unlock(key);
    }
    return true;
  }
}

redis的消息隊列涤躲,只做特性介紹携添,公司實際使用rocketmq

  • 使用簡單,只有一個消費者

  • 沒有ack保證篓叶,如果對消息可靠性有極高的要求,推薦使用rocketmq的事務(wù)消息

  • 使用list作為異步消息的發(fā)送結(jié)構(gòu)羞秤,rpush缸托,rpop

延時隊列的實現(xiàn)

  • 延時隊列可以使用zset實現(xiàn),將消息序列化成為一個字符串瘾蛋,消息的到期時間為score俐镐。然后使用多個線程去輪詢這個隊列。使用多線程是為了保障可用性哺哼。一個線程掛了佩抹,還有其他線程可以進行處理

位圖,節(jié)約存儲空間

  • 平時進行開發(fā)的時候取董,要對一些bool類型的數(shù)據(jù)進行存取棍苹,如用戶一年的簽到記錄。如果使用key/value方式進行存取茵汰,就要記錄365個枢里,當(dāng)用戶量上億的時候,需要的存儲結(jié)構(gòu)是非常驚人的蹂午。

  • redis提供了位圖數(shù)據(jù)結(jié)構(gòu)栏豺,每個用戶每天的簽到就只占據(jù)一個位。365天大概需要46個字節(jié)豆胸,大大節(jié)約空間

  • 位圖就是byte數(shù)組奥洼,可以使用getbit和setbit進行存儲,如果字節(jié)是不可以打印字符,redis會展示其16進制晚胡。實際來操作一波

零存整取的方式灵奖,一個個bit去設(shè)置,然后一次性拿出值
通過python獲取字母`h`的hash值為0b1101000
setbit s 1 1
setbit s 2 1
setbit s 4 1
get s
"h"      # 這樣就得到了h的值

零存零取的方式估盘,就是對單個bit進行操作,沒有設(shè)置的位就是默認(rèn)為0
set bit w 1 1
get bit w 0
0

整存零取
set w h
getbit w 1
1

不可打印字符
setbit x 0 1
setbit x 1 1
get x
"\xc0"
  • 位圖的統(tǒng)計和查找:redis提供了對位圖的基本統(tǒng)計算
 set w hello
 ok
 bitcount w
 21
 bitcount w 0 0
 3
 bitcount w 0 1  # 統(tǒng)計1的數(shù)量
 7
 bitpos w 0  # 第一個0
 0

 bitpos w 1 1 1 # 第二個字符算起桑寨,第一個1位
 魔術(shù)指令bitfield
 bitfield w get u4 0

高級數(shù)據(jù)結(jié)構(gòu)

HyberLogLog,提供不精確的統(tǒng)計數(shù)據(jù)

  • 頁面的uv,不能使用一個key的incr忿檩,因為要進行去重

  • 使用zset也可以做到這個尉尾,但是消耗空間太大

  • HyperLogLog的標(biāo)準(zhǔn)誤差是0.81%。對于不精確的統(tǒng)計燥透,可以使用這個數(shù)據(jù)結(jié)構(gòu)

  • 提供pdadd沙咏,pfcount兩個指令,pf是Philippe Flajolet教授的名字縮寫辨图,這個數(shù)據(jù)結(jié)構(gòu)是他發(fā)明的。這個數(shù)據(jù)結(jié)構(gòu)占用的內(nèi)存是12k肢藐,使用了2^14個桶去進行計算


pfadd book java
1
pfadd book java
0
pfadd book python
1
pfadd book python
pfcount book
2

布隆過濾器Bloom Filter

  • 這個數(shù)據(jù)結(jié)構(gòu)主要實現(xiàn)去重的功能

  • 場景使用:廣告推送的時候故河,如果我們看過了一條廣告,下一條推送的時候吆豹,要對看過的廣告進行去重鱼的。如果使用關(guān)系型數(shù)據(jù)庫,每次推送都要去數(shù)據(jù)庫查詢痘煤,這樣數(shù)據(jù)庫很難抗住這個壓力凑阶。況且,瀏覽記錄會隨著時間線性增長衷快,空間也跟不上

  • 布隆過濾器會有一定的誤判概率宙橱。這可以比喻成為一個不精確的set結(jié)構(gòu),調(diào)用contains的時候蘸拔,有可能誤判师郑。如果說這個值不存在,那就肯定不存在调窍。如果說這個值存在宝冕,優(yōu)肯是誤判,可能邓萨,兩者長得相像猬仁?這樣的話,對于實際業(yè)務(wù)也沒有影響先誉,我們可以保證推送給用戶的都是最新的內(nèi)容湿刽,只有極少數(shù)用戶沒看過的內(nèi)容會被誤判。經(jīng)過測試褐耳,誤判率大概在1%左右诈闺。調(diào)整參數(shù)可以優(yōu)化這個誤判率

  • 布隆過濾器作為redis的一個插件,需要版本為4.0以后铃芦,才能使用雅镊。

  • 基本指令為bf.add,bf.exists刃滓,bf.madd(批量)仁烹,安裝docker,然后去重新安裝redis咧虎,帶這個插件的版本卓缰。centos7安裝docker方法,安裝好以后再安裝插件,使用docker啟動redis

  • 用戶可以通過bf.reserve命令顯式制定錯誤率征唬,key和初始空間捌显。錯誤率越低,占用空間越大总寒。初始值越大扶歪,空間越大,所以摄闸,在使用的時候善镰,需要準(zhǔn)確預(yù)估一下元素的大小。

  • 布隆過濾器是一個大型的位數(shù)組年枕。向布隆過濾器添加key的時候炫欺,會使用多個hash函數(shù)對key進行hash,算得一個整數(shù)索引画切。然后對位數(shù)組的長度取模運算得到一個位置,每個hash函數(shù)都得到一個不同的位置囱怕,再把這幾個位置都設(shè)置為1霍弹,完成了add操作。巧妙地共享可多個key的存儲空間娃弓。

  • 為何會出現(xiàn)誤判呢典格?就是由于判斷key是否存在的時候,如果某個位是0台丛,那就一定不存在耍缴。但是,如果各個位置都是1的話挽霉,也不能說明是存在的防嗡,有可能是其他key存在導(dǎo)致。如果位數(shù)組比較稀疏侠坎,出現(xiàn)誤判的概率就低了蚁趁。所以,使用的時候实胸,不要讓實際的元素大于初始值他嫡。如果大于的時候,要對布隆過濾器進行重建庐完。

  • set存儲的是所有的元素钢属,布隆過濾器是存儲元素的指紋(hash)∶徘空間節(jié)省還是比較明顯的.

  • docker加載插件的命令淆党,docker run -p 6379:6379 --name redis-redisbloom redislabs/rebloom:latest

  • docker exec -it redis-redisbloom bash

bf.add book java
1
bf.add book java   #再次加入java,就被排出了
0
bf.exists book python
0

redis限流

  • 后端屏蔽用戶的過于頻繁的請求,比如1秒內(nèi)不能請求超過5次宁否。這個限流存在一個滑動時間窗口窒升,zset的score圈出一個時間窗口,我們保留這個時間窗口慕匠,對于窗口外面的數(shù)據(jù)饱须,則可以統(tǒng)統(tǒng)砍掉。zset的value可以為毫秒時間戳台谊。

public class SimpleRateLimiter{
  private Jedis jedis;

  //用戶請求是否允許

  //使用樂觀鎖

  //value保證唯一性就好

  public boolean isActionAllow(String userId, String actionKey, int period, int maxCount) {
    String key = String.format("hist:%s:%s", userId, actionKey);
    long now = System.currentTimeMillis();
    Pipeline pipe = jedis.pipelined();
    pipe.multi();
    pipe.zadd(key, now, "", now);

    //截取
    pipe.zremrangeByScore(key, 0, now - period * 1000);
    Response<Long> count = pipe.zcard(key);
    pipe.expire(key, period + 1);
    pipe.exec();
    pipe.close();
    return count.get() <= maxCount;
  }
}
  • 漏斗限流蓉媳,靈感來源于漏斗,就是漏斗有一個容量锅铅,也有一個消耗速率酪呻。當(dāng)灌水速率小于漏水速率,漏斗超過容量以后盐须。灌水行為就要丟棄或者暫停

//單機版的漏斗限流

public class FunnelRateLimiter{
  class Funnel{
    //容量
    int capacity;
    //漏水速率
    float leakingRate;
    //剩余空間
    int leftQuota;
    //上次觸發(fā)時間
    long leakingTs;
  }

  //....省略構(gòu)造方法

  void makeSpace() {
    long now = System.currentTimeMillis();
    long deltaTs = nowTs - leakingTs;
    int deltaQuota = (int)(deltaTs * leakingRate);
    //如果時間間隔太久玩荠,可能會導(dǎo)致int溢出
    if (deltaQuota < 0) {
      //重設(shè)
      this.leftQuota = capacity;
      this.leakingTs = 0;
      return;
    }

    if (deltaQuota < 1) {
      return;
    }

    this.leftQuota += leftQuota;
    this.leakingTs = nowTs;
    if (this.leftQuota > this.capacity) {
      this.leftQuota = this.capacity;
    }

    //是否可以接收請求

    boolean watering(int quota) {
      makeSpace();
      if (leftQuota >= quota) {
        this.leftQuota -= quota;
        return true;
      }
      return false;
    }
  }

  private Map<String, Funnel> funnels = new HashMap<>();
  public boolean isActionAllow(String userId, String actionKey, int capacity, fload leakingRate) {
    String key = String.format("funnels:%s:%s", userId, actionKey);
    Funnel funnel = funnels.get(key);
    if (funnel == null) {
      funnel = new Funnel(capacity, leakingRate);
      funnels.put(key, funnel);
    }
    return funnel.watering(1);
  }
}

  • 這里是redis提供的分布式限流,redis4.0提供了redis-cell贼邓。這是一個差價阶冈,需要去github下載redis-cell的源碼解壓以后。進入redis-cli后塑径,執(zhí)行redis的 module load命令即可加載
wget https://github.com/brandur/redis-cell/releases/download/v0.2.4/redis-cell-v0.2.4-x86_64-unknown-linux-gnu.tar.gz

tar -xzvf redis-cell-v0.2.4-x86_64-unknown-linux-gnu.tar.gz

解壓后得到libredis_cess.so

redis-cli

module load /data/libredis_cell.so

我這里是放到了data目錄下面女坑,運行redis后加載就行了

//各個參數(shù)    key    容量          計算漏斗速率              可選參數(shù)

cl.throttle <key> <max_burst> <count per period> <period> [<quantity>]

cl.throttle book 15 30 60 1  # 這個表示容量為15,2秒一個漏水速率(30/60)

0          # 0 允許 -1 拒絕

16        # 漏斗容量

15        # 剩余空間

-1        # 當(dāng)?shù)谝粋€參數(shù)為-1師,這里的值表示需要經(jīng)過多少時間以后才能再試

2          # 多久時間以后统舀,漏斗可以完全空出來

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末匆骗,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子誉简,更是在濱河造成了極大的恐慌碉就,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闷串,死亡現(xiàn)場離奇詭異铝噩,居然都是意外死亡,警方通過查閱死者的電腦和手機窿克,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進店門骏庸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人年叮,你說我怎么就攤上這事具被。” “怎么了只损?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵一姿,是天一觀的道長七咧。 經(jīng)常有香客問我,道長叮叹,這世上最難降的妖魔是什么艾栋? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮蛉顽,結(jié)果婚禮上蝗砾,老公的妹妹穿的比我還像新娘。我一直安慰自己携冤,他們只是感情好悼粮,可當(dāng)我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著曾棕,像睡著了一般扣猫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上翘地,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天申尤,我揣著相機與錄音,去河邊找鬼衙耕。 笑死昧穿,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的臭杰。 我是一名探鬼主播粤咪,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼谚中,長吁一口氣:“原來是場噩夢啊……” “哼渴杆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起宪塔,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤磁奖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后某筐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體比搭,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年南誊,在試婚紗的時候發(fā)現(xiàn)自己被綠了身诺。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡抄囚,死狀恐怖霉赡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情幔托,我是刑警寧澤穴亏,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布蜂挪,位于F島的核電站,受9級特大地震影響嗓化,放射性物質(zhì)發(fā)生泄漏棠涮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一刺覆、第九天 我趴在偏房一處隱蔽的房頂上張望严肪。 院中可真熱鬧,春花似錦隅津、人聲如沸诬垂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽结窘。三九已至,卻和暖如春充蓝,著一層夾襖步出監(jiān)牢的瞬間隧枫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工谓苟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留官脓,地道東北人。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓涝焙,卻偏偏與公主長得像卑笨,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子仑撞,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,452評論 2 348

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