Redis常見面試題(精簡版)

什么是Redis


Redis(Remote Dictionary Server) 是一個使用 C 語言編寫的,開源的(BSD許可)高性能非關(guān)系型(NoSQL)的鍵值對數(shù)據(jù)庫。

Redis 可以存儲鍵和五種不同類型的值之間的映射。鍵的類型只能為字符串,值支持五種數(shù)據(jù)類型:字符串徽龟、列表、集合斋荞、散列表、有序集合胎撤。

與傳統(tǒng)數(shù)據(jù)庫不同的是 Redis 的數(shù)據(jù)是存在內(nèi)存中的,所以讀寫速度非澈拦瑁快哩照,因此 redis 被廣泛應(yīng)用于緩存方向,每秒可以處理超過 10萬次讀寫操作懒浮,是已知性能最快的Key-Value DB飘弧。另外识藤,Redis 也經(jīng)常用來做分布式鎖。除此之外次伶,Redis 支持事務(wù) 痴昧、持久化、LUA腳本冠王、LRU驅(qū)動事件赶撰、多種集群方案。

Redis有哪些優(yōu)缺點

優(yōu)點

讀寫性能優(yōu)異柱彻, Redis能讀的速度是110000次/s豪娜,寫的速度是81000次/s。

支持數(shù)據(jù)持久化哟楷,支持AOF和RDB兩種持久化方式瘤载。

支持事務(wù),Redis的所有操作都是原子性的卖擅,同時Redis還支持對幾個操作合并后的原子性執(zhí)行鸣奔。

數(shù)據(jù)結(jié)構(gòu)豐富,除了支持string類型的value外還支持hash惩阶、set挎狸、zset、list等數(shù)據(jù)結(jié)構(gòu)断楷。

支持主從復(fù)制锨匆,主機會自動將數(shù)據(jù)同步到從機,可以進行讀寫分離脐嫂。

缺點

數(shù)據(jù)庫容量受到物理內(nèi)存的限制统刮,不能用作海量數(shù)據(jù)的高性能讀寫紊遵,因此Redis適合的場景主要局限在較小數(shù)據(jù)量的高性能操作和運算上账千。

Redis 不具備自動容錯和恢復(fù)功能,主機從機的宕機都會導(dǎo)致前端部分讀寫請求失敗暗膜,需要等待機器重啟或者手動切換前端的IP才能恢復(fù)匀奏。

主機宕機,宕機前有部分數(shù)據(jù)未能及時同步到從機学搜,切換IP后還會引入數(shù)據(jù)不一致的問題娃善,降低了系統(tǒng)的可用性。

Redis 較難支持在線擴容瑞佩,在集群容量達到上限時在線擴容會變得很復(fù)雜聚磺。為避免這一問題,運維人員在系統(tǒng)上線時必須確保有足夠的空間炬丸,這對資源造成了很大的浪費瘫寝。

為什么要用 Redis /為什么要用緩存

主要從“高性能”和“高并發(fā)”這兩點來看待這個問題蜒蕾。

高性能:

假如用戶第一次訪問數(shù)據(jù)庫中的某些數(shù)據(jù)。這個過程會比較慢焕阿,因為是從硬盤上讀取的咪啡。將該用戶訪問的數(shù)據(jù)存在數(shù)緩存中,這樣下一次再訪問這些數(shù)據(jù)的時候就可以直接從緩存中獲取了暮屡。操作緩存就是直接操作內(nèi)存撤摸,所以速度相當(dāng)快。如果數(shù)據(jù)庫中的對應(yīng)數(shù)據(jù)改變的之后褒纲,同步改變緩存中相應(yīng)的數(shù)據(jù)即可准夷!

高并發(fā):

直接操作緩存能夠承受的請求是遠遠大于直接訪問數(shù)據(jù)庫的,所以我們可以考慮把數(shù)據(jù)庫中的部分數(shù)據(jù)轉(zhuǎn)移到緩存中去莺掠,這樣用戶的一部分請求會直接到緩存這里而不用經(jīng)過數(shù)據(jù)庫冕象。

為什么要用 Redis 而不用 map/guava 做緩存?

緩存分為本地緩存和分布式緩存。以 Java 為例汁蝶,使用自帶的 map 或者 guava 實現(xiàn)的是本地緩存渐扮,最主要的特點是輕量以及快速,生命周期隨著 jvm 的銷毀而結(jié)束掖棉,并且在多實例的情況下墓律,每個實例都需要各自保存一份緩存,緩存不具有一致性幔亥。

使用 redis 或 memcached 之類的稱為分布式緩存耻讽,在多實例的情況下,各實例共用一份緩存數(shù)據(jù)帕棉,緩存具有一致性针肥。缺點是需要保持 redis 或 memcached服務(wù)的高可用,整個程序架構(gòu)上較為復(fù)雜香伴。

Redis為什么這么快

1慰枕、完全基于內(nèi)存,絕大部分請求是純粹的內(nèi)存操作即纲,非尘甙铮快速。數(shù)據(jù)存在內(nèi)存中低斋,類似于 HashMap蜂厅,HashMap 的優(yōu)勢就是查找和操作的時間復(fù)雜度都是O(1);

2膊畴、數(shù)據(jù)結(jié)構(gòu)簡單掘猿,對數(shù)據(jù)操作也簡單,Redis 中的數(shù)據(jù)結(jié)構(gòu)是專門進行設(shè)計的唇跨;

3稠通、采用單線程礁遵,避免了不必要的上下文切換和競爭條件,也不存在多進程或者多線程導(dǎo)致的切換而消耗 CPU采记,不用去考慮各種鎖的問題佣耐,不存在加鎖釋放鎖操作,沒有因為可能出現(xiàn)死鎖而導(dǎo)致的性能消耗唧龄;

4兼砖、使用多路 I/O 復(fù)用模型,非阻塞 IO既棺;

5讽挟、使用底層模型不同,它們之間底層實現(xiàn)方式以及與客戶端之間通信的應(yīng)用協(xié)議不一樣丸冕,Redis 直接自己構(gòu)建了 VM 機制 耽梅,因為一般的系統(tǒng)調(diào)用系統(tǒng)函數(shù)的話,會浪費一定的時間去移動和請求胖烛;

數(shù)據(jù)類型

Redis有哪些數(shù)據(jù)類型

Redis主要有5種數(shù)據(jù)類型眼姐,包括String,List佩番,Set众旗,Zset,Hash趟畏,滿足大部分的使用要求

數(shù)據(jù)類型可以存儲的值操作應(yīng)用場景

STRING字符串贡歧、整數(shù)或者浮點數(shù)對整個字符串或者字符串的其中一部分執(zhí)行操作

對整數(shù)和浮點數(shù)執(zhí)行自增或者自減操作

做簡單的鍵值對緩存

LIST列表從兩端壓入或者彈出元素

對單個或者多個元素進行修剪,

只保留一個范圍內(nèi)的元素

存儲一些列表型的數(shù)據(jù)結(jié)構(gòu)赋秀,類似粉絲列表利朵、文章的評論列表之類的數(shù)據(jù)

SET無序集合添加、獲取猎莲、移除單個元素

檢查一個元素是否存在于集合中

計算交集绍弟、并集、差集

從集合里面隨機獲取元素

交集益眉、并集晌柬、差集的操作姥份,比如交集郭脂,可以把兩個人的粉絲列表整一個交集

HASH包含鍵值對的無序散列表添加、獲取澈歉、移除單個鍵值對

獲取所有鍵值對

檢查某個鍵是否存在

結(jié)構(gòu)化的數(shù)據(jù)展鸡,比如一個對象

ZSET有序集合添加、獲取埃难、刪除元素

根據(jù)分值范圍或者成員來獲取元素

計算一個鍵的排名

去重但可以排序莹弊,如獲取排名前幾名的用戶

Redis的應(yīng)用場景

總結(jié)一

計數(shù)器

可以對 String 進行自增自減運算涤久,從而實現(xiàn)計數(shù)器功能。Redis 這種內(nèi)存型數(shù)據(jù)庫的讀寫性能非常高忍弛,很適合存儲頻繁讀寫的計數(shù)量响迂。

緩存

將熱點數(shù)據(jù)放到內(nèi)存中,設(shè)置內(nèi)存的最大使用量以及淘汰策略來保證緩存的命中率细疚。

會話緩存

可以使用 Redis 來統(tǒng)一存儲多臺應(yīng)用服務(wù)器的會話信息蔗彤。當(dāng)應(yīng)用服務(wù)器不再存儲用戶的會話信息,也就不再具有狀態(tài)疯兼,一個用戶可以請求任意一個應(yīng)用服務(wù)器然遏,從而更容易實現(xiàn)高可用性以及可伸縮性。

全頁緩存(FPC)

除基本的會話token之外吧彪,Redis還提供很簡便的FPC平臺待侵。以Magento為例,Magento提供一個插件來使用Redis作為全頁緩存后端姨裸。此外秧倾,對WordPress的用戶來說,Pantheon有一個非常好的插件 wp-redis傀缩,這個插件能幫助你以最快速度加載你曾瀏覽過的頁面中狂。

查找表

例如 DNS 記錄就很適合使用 Redis 進行存儲。查找表和緩存類似扑毡,也是利用了 Redis 快速的查找特性胃榕。但是查找表的內(nèi)容不能失效,而緩存的內(nèi)容可以失效瞄摊,因為緩存不作為可靠的數(shù)據(jù)來源勋又。

消息隊列(發(fā)布/訂閱功能)

List 是一個雙向鏈表,可以通過 lpush 和 rpop 寫入和讀取消息换帜。不過最好使用 Kafka楔壤、RabbitMQ 等消息中間件。

分布式鎖實現(xiàn)

在分布式場景下惯驼,無法使用單機環(huán)境下的鎖來對多個節(jié)點上的進程進行同步蹲嚣。可以使用 Redis 自帶的 SETNX 命令實現(xiàn)分布式鎖祟牲,除此之外隙畜,還可以使用官方提供的 RedLock 分布式鎖實現(xiàn)。

其它

Set 可以實現(xiàn)交集说贝、并集等操作议惰,從而實現(xiàn)共同好友等功能。ZSet 可以實現(xiàn)有序性操作乡恕,從而實現(xiàn)排行榜等功能言询。

總結(jié)二

Redis相比其他緩存俯萎,有一個非常大的優(yōu)勢,就是支持多種數(shù)據(jù)類型运杭。

數(shù)據(jù)類型說明string字符串夫啊,最簡單的k-v存儲hashhash格式,value為field和value辆憔,適合ID-Detail這樣的場景涮母。list簡單的list,順序列表躁愿,支持首位或者末尾插入數(shù)據(jù)set無序list叛本,查找速度快,適合交集彤钟、并集来候、差集處理sorted set有序的set

其實,通過上面的數(shù)據(jù)類型的特性逸雹,基本就能想到合適的應(yīng)用場景了营搅。

string——適合最簡單的k-v存儲,類似于memcached的存儲結(jié)構(gòu)梆砸,短信驗證碼转质,配置信息等,就用這種類型來存儲帖世。

hash——一般key為ID或者唯一標示休蟹,value對應(yīng)的就是詳情了。如商品詳情日矫,個人信息詳情赂弓,新聞詳情等。

list——因為list是有序的哪轿,比較適合存儲一些有序且數(shù)據(jù)相對固定的數(shù)據(jù)盈魁。如省市區(qū)表、字典表等窃诉。因為list是有序的杨耙,適合根據(jù)寫入的時間來排序,如:最新的***飘痛,消息隊列等珊膜。

set——可以簡單的理解為ID-List的模式,如微博中一個人有哪些好友敦冬,set最牛的地方在于辅搬,可以對兩個set提供交集、并集脖旱、差集操作堪遂。例如:查找兩個人共同的好友等。

Sorted Set——是set的增強版本萌庆,增加了一個score參數(shù)溶褪,自動會根據(jù)score的值進行排序。比較適合類似于top 10等不根據(jù)插入的時間來排序的數(shù)據(jù)践险。

如上所述猿妈,雖然Redis不像關(guān)系數(shù)據(jù)庫那么復(fù)雜的數(shù)據(jù)結(jié)構(gòu),但是巍虫,也能適合很多場景彭则,比一般的緩存數(shù)據(jù)結(jié)構(gòu)要多。了解每種數(shù)據(jù)結(jié)構(gòu)適合的業(yè)務(wù)場景占遥,不僅有利于提升開發(fā)效率俯抖,也能有效利用Redis的性能。

持久化

什么是Redis持久化瓦胎?

持久化就是把內(nèi)存的數(shù)據(jù)寫到磁盤中去芬萍,防止服務(wù)宕機了內(nèi)存數(shù)據(jù)丟失。

Redis 的持久化機制是什么搔啊?各自的優(yōu)缺點柬祠?

Redis 提供兩種持久化機制 RDB(默認) 和 AOF 機制:

RDB:是Redis DataBase縮寫快照

RDB是Redis默認的持久化方式揖庄。按照一定的時間將內(nèi)存的數(shù)據(jù)以快照的形式保存到硬盤中过蹂,對應(yīng)產(chǎn)生的數(shù)據(jù)文件為dump.rdb救赐。通過配置文件中的save參數(shù)來定義快照的周期痪宰。

優(yōu)點:

1暴备、只有一個文件 dump.rdb督赤,方便持久化霉颠。

2猬仁、容災(zāi)性好蚜点,一個文件可以保存到安全的磁盤轧房。

3、性能最大化绍绘,fork 子進程來完成寫操作奶镶,讓主進程繼續(xù)處理命令,所以是 IO 最大化陪拘。使用單獨子進程來進行持久化厂镇,主進程不會進行任何 IO 操作,保證了 redis 的高性能

4.相對于數(shù)據(jù)集大時左刽,比 AOF 的啟動效率更高捺信。

缺點:

1、數(shù)據(jù)安全性低。RDB 是間隔一段時間進行持久化迄靠,如果持久化之間 redis 發(fā)生故障秒咨,會發(fā)生數(shù)據(jù)丟失。所以這種方式更適合數(shù)據(jù)要求不嚴謹?shù)臅r候)

2掌挚、AOF(Append-only file)持久化方式: 是指所有的命令行記錄以 redis 命令請 求協(xié)議的格式完全持久化存儲)保存為 aof 文件雨席。

AOF:持久化

AOF持久化(即Append Only File持久化),則是將Redis執(zhí)行的每次寫命令記錄到單獨的日志文件中吠式,當(dāng)重啟Redis會重新將持久化的日志中文件恢復(fù)數(shù)據(jù)陡厘。

當(dāng)兩種方式同時開啟時,數(shù)據(jù)恢復(fù)Redis會優(yōu)先選擇AOF恢復(fù)特占。

優(yōu)點:

1糙置、數(shù)據(jù)安全,aof 持久化可以配置 appendfsync 屬性是目,有 always谤饭,每進行一次 命令操作就記錄到 aof 文件中一次。

2胖笛、通過 append 模式寫文件网持,即使中途服務(wù)器宕機,可以通過 redis-check-aof 工具解決數(shù)據(jù)一致性問題长踊。

3功舀、AOF 機制的 rewrite 模式。AOF 文件沒被 rewrite 之前(文件過大時會對命令 進行合并重寫)身弊,可以刪除其中的某些命令(比如誤操作的 flushall))

缺點:

1辟汰、AOF 文件比 RDB 文件大,且恢復(fù)速度慢阱佛。

2帖汞、數(shù)據(jù)集大的時候,比 rdb 啟動效率低凑术。

優(yōu)缺點是什么翩蘸?

AOF文件比RDB更新頻率高,優(yōu)先使用AOF還原數(shù)據(jù)淮逊。

AOF比RDB更安全也更大

RDB性能比AOF好

如果兩個都配了優(yōu)先加載AOF

如何選擇合適的持久化方式

一般來說催首, 如果想達到足以媲美PostgreSQL的數(shù)據(jù)安全性,你應(yīng)該同時使用兩種持久化功能泄鹏。在這種情況下郎任,當(dāng) Redis 重啟的時候會優(yōu)先載入AOF文件來恢復(fù)原始的數(shù)據(jù),因為在通常情況下AOF文件保存的數(shù)據(jù)集要比RDB文件保存的數(shù)據(jù)集要完整备籽。

如果你非常關(guān)心你的數(shù)據(jù)舶治, 但仍然可以承受數(shù)分鐘以內(nèi)的數(shù)據(jù)丟失,那么你可以只使用RDB持久化。

有很多用戶都只使用AOF持久化霉猛,但并不推薦這種方式尺锚,因為定時生成RDB快照(snapshot)非常便于進行數(shù)據(jù)庫備份, 并且 RDB 恢復(fù)數(shù)據(jù)集的速度也要比AOF恢復(fù)的速度要快韩脏,除此之外缩麸,使用RDB還可以避免AOF程序的bug铸磅。

如果你只希望你的數(shù)據(jù)在服務(wù)器運行的時候存在赡矢,你也可以不使用任何持久化方式。

Redis持久化數(shù)據(jù)和緩存怎么做擴容阅仔?

如果Redis被當(dāng)做緩存使用吹散,使用一致性哈希實現(xiàn)動態(tài)擴容縮容。

如果Redis被當(dāng)做一個持久化存儲使用八酒,必須使用固定的keys-to-nodes映射關(guān)系空民,節(jié)點的數(shù)量一旦確定不能變化。否則的話(即Redis節(jié)點需要動態(tài)變化的情況)羞迷,必須使用可以在運行時進行數(shù)據(jù)再平衡的一套系統(tǒng)界轩,而當(dāng)前只有Redis集群可以做到這樣。

過期鍵的刪除策略

Redis的過期鍵的刪除策略

我們都知道衔瓮,Redis是key-value數(shù)據(jù)庫浊猾,我們可以設(shè)置Redis中緩存的key的過期時間。Redis的過期策略就是指當(dāng)Redis中緩存的key過期了热鞍,Redis如何處理葫慎。

過期策略通常有以下三種:

定時過期:每個設(shè)置過期時間的key都需要創(chuàng)建一個定時器,到過期時間就會立即清除薇宠。該策略可以立即清除過期的數(shù)據(jù)偷办,對內(nèi)存很友好;但是會占用大量的CPU資源去處理過期的數(shù)據(jù)澄港,從而影響緩存的響應(yīng)時間和吞吐量椒涯。

惰性過期:只有當(dāng)訪問一個key時,才會判斷該key是否已過期回梧,過期則清除废岂。該策略可以最大化地節(jié)省CPU資源,卻對內(nèi)存非常不友好漂辐。極端情況可能出現(xiàn)大量的過期key沒有再次被訪問泪喊,從而不會被清除,占用大量內(nèi)存髓涯。

定期過期:每隔一定的時間袒啼,會掃描一定數(shù)量的數(shù)據(jù)庫的expires字典中一定數(shù)量的key,并清除其中已過期的key。該策略是前兩者的一個折中方案蚓再。通過調(diào)整定時掃描的時間間隔和每次掃描的限定耗時滑肉,可以在不同情況下使得CPU和內(nèi)存資源達到最優(yōu)的平衡效果。

(expires字典會保存所有設(shè)置了過期時間的key的過期時間數(shù)據(jù)摘仅,其中靶庙,key是指向鍵空間中的某個鍵的指針,value是該鍵的毫秒精度的UNIX時間戳表示的過期時間娃属。鍵空間是指該Redis集群中保存的所有鍵六荒。)

Redis中同時使用了惰性過期和定期過期兩種過期策略。

Redis key的過期時間和永久有效分別怎么設(shè)置矾端?

EXPIRE和PERSIST命令掏击。

我們知道通過expire來設(shè)置key 的過期時間,那么對過期的數(shù)據(jù)怎么處理呢?

除了緩存服務(wù)器自帶的緩存失效策略之外(Redis默認的有6中策略可供選擇)秩铆,我們還可以根據(jù)具體的業(yè)務(wù)需求進行自定義的緩存淘汰砚亭,常見的策略有兩種:

定時去清理過期的緩存;

當(dāng)有用戶請求過來時殴玛,再判斷這個請求所用到的緩存是否過期捅膘,過期的話就去底層系統(tǒng)得到新數(shù)據(jù)并更新緩存。

兩者各有優(yōu)劣滚粟,第一種的缺點是維護大量緩存的key是比較麻煩的寻仗,第二種的缺點就是每次用戶請求過來都要判斷緩存失效,邏輯相對比較復(fù)雜坦刀!具體用哪種方案愧沟,大家可以根據(jù)自己的應(yīng)用場景來權(quán)衡。

內(nèi)存相關(guān)

MySQL里有2000w數(shù)據(jù)鲤遥,redis中只存20w的數(shù)據(jù)沐寺,如何保證redis中的數(shù)據(jù)都是熱點數(shù)據(jù)

redis內(nèi)存數(shù)據(jù)集大小上升到一定大小的時候,就會施行數(shù)據(jù)淘汰策略盖奈。

Redis的內(nèi)存淘汰策略有哪些

Redis的內(nèi)存淘汰策略是指在Redis的用于緩存的內(nèi)存不足時混坞,怎么處理需要新寫入且需要申請額外空間的數(shù)據(jù)。

全局的鍵空間選擇性移除

noeviction:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時钢坦,新寫入操作會報錯究孕。

allkeys-lru:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時,在鍵空間中爹凹,移除最近最少使用的key厨诸。(這個是最常用的)

allkeys-random:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時,在鍵空間中禾酱,隨機移除某個key微酬。

設(shè)置過期時間的鍵空間選擇性移除

volatile-lru:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時绘趋,在設(shè)置了過期時間的鍵空間中,移除最近最少使用的key颗管。

volatile-random:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時陷遮,在設(shè)置了過期時間的鍵空間中,隨機移除某個key垦江。

volatile-ttl:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時帽馋,在設(shè)置了過期時間的鍵空間中,有更早過期時間的key優(yōu)先移除比吭。

總結(jié)

Redis的內(nèi)存淘汰策略的選取并不會影響過期的key的處理绽族。內(nèi)存淘汰策略用于處理內(nèi)存不足時的需要申請額外空間的數(shù)據(jù);過期策略用于處理過期的緩存數(shù)據(jù)梗逮。

Redis主要消耗什么物理資源项秉?

內(nèi)存绣溜。

Redis的內(nèi)存用完了會發(fā)生什么慷彤?

如果達到設(shè)置的上限,Redis的寫命令會返回錯誤信息(但是讀命令還可以正常返回怖喻。)或者你可以配置內(nèi)存淘汰機制底哗,當(dāng)Redis達到內(nèi)存上限時會沖刷掉舊的內(nèi)容。

Redis如何做內(nèi)存優(yōu)化锚沸?

可以好好利用Hash,list,sorted set,set等集合類型數(shù)據(jù)跋选,因為通常情況下很多小的Key-Value可以用更緊湊的方式存放到一起。盡可能使用散列表(hashes)哗蜈,散列表(是說散列表里面存儲的數(shù)少)使用的內(nèi)存非常小前标,所以你應(yīng)該盡可能的將你的數(shù)據(jù)模型抽象到一個散列表里面。比如你的web系統(tǒng)中有一個用戶對象距潘,不要為這個用戶的名稱炼列,姓氏,郵箱音比,密碼設(shè)置單獨的key俭尖,而是應(yīng)該把這個用戶的所有信息存儲到一張散列表里面

線程模型

Redis線程模型

Redis基于Reactor模式開發(fā)了網(wǎng)絡(luò)事件處理器,這個處理器被稱為文件事件處理器(file event handler)洞翩。它的組成結(jié)構(gòu)為4部分:多個套接字稽犁、IO多路復(fù)用程序、文件事件分派器骚亿、事件處理器已亥。因為文件事件分派器隊列的消費是單線程的,所以Redis才叫單線程模型来屠。

文件事件處理器使用 I/O 多路復(fù)用(multiplexing)程序來同時監(jiān)聽多個套接字虑椎, 并根據(jù)套接字目前執(zhí)行的任務(wù)來為套接字關(guān)聯(lián)不同的事件處理器秫舌。

當(dāng)被監(jiān)聽的套接字準備好執(zhí)行連接應(yīng)答(accept)、讀刃迕省(read)足陨、寫入(write)、關(guān)閉(close)等操作時娇未, 與操作相對應(yīng)的文件事件就會產(chǎn)生墨缘, 這時文件事件處理器就會調(diào)用套接字之前關(guān)聯(lián)好的事件處理器來處理這些事件。

雖然文件事件處理器以單線程方式運行零抬, 但通過使用 I/O 多路復(fù)用程序來監(jiān)聽多個套接字镊讼, 文件事件處理器既實現(xiàn)了高性能的網(wǎng)絡(luò)通信模型, 又可以很好地與 redis 服務(wù)器中其他同樣以單線程方式運行的模塊進行對接平夜, 這保持了 Redis 內(nèi)部單線程設(shè)計的簡單性蝶棋。

參考:https://www.cnblogs.com/barrywxx/p/8570821.html

事務(wù)

什么是事務(wù)?

事務(wù)是一個單獨的隔離操作:事務(wù)中的所有命令都會序列化忽妒、按順序地執(zhí)行玩裙。事務(wù)在執(zhí)行的過程中,不會被其他客戶端發(fā)送來的命令請求所打斷段直。

事務(wù)是一個原子操作:事務(wù)中的命令要么全部被執(zhí)行吃溅,要么全部都不執(zhí)行。

Redis事務(wù)的概念

Redis 事務(wù)的本質(zhì)是通過MULTI鸯檬、EXEC决侈、WATCH等一組命令的集合。事務(wù)支持一次執(zhí)行多個命令喧务,一個事務(wù)中所有命令都會被序列化赖歌。在事務(wù)執(zhí)行過程,會按照順序串行化執(zhí)行隊列中的命令功茴,其他客戶端提交的命令請求不會插入到事務(wù)執(zhí)行命令序列中庐冯。

總結(jié)說:redis事務(wù)就是一次性、順序性痊土、排他性的執(zhí)行一個隊列中的一系列命令肄扎。

Redis事務(wù)的三個階段

事務(wù)開始 MULTI

命令入隊

事務(wù)執(zhí)行 EXEC

事務(wù)執(zhí)行過程中,如果服務(wù)端收到有EXEC赁酝、DISCARD犯祠、WATCH、MULTI之外的請求酌呆,將會把請求放入隊列中排隊

Redis事務(wù)相關(guān)命令

Redis事務(wù)功能是通過MULTI衡载、EXEC、DISCARD和WATCH 四個原語實現(xiàn)的

Redis會將一個事務(wù)中的所有命令序列化隙袁,然后按順序執(zhí)行痰娱。

redis 不支持回滾弃榨,“Redis 在事務(wù)失敗時不進行回滾,而是繼續(xù)執(zhí)行余下的命令”梨睁, 所以 Redis 的內(nèi)部可以保持簡單且快速鲸睛。

如果在一個事務(wù)中的命令出現(xiàn)錯誤,那么所有的命令都不會執(zhí)行坡贺;

如果在一個事務(wù)中出現(xiàn)運行錯誤官辈,那么正確的命令會被執(zhí)行

WATCH 命令是一個樂觀鎖遍坟,可以為 Redis 事務(wù)提供 check-and-set (CAS)行為拳亿。 可以監(jiān)控一個或多個鍵,一旦其中有一個鍵被修改(或刪除)愿伴,之后的事務(wù)就不會執(zhí)行肺魁,監(jiān)控一直持續(xù)到EXEC命令。

MULTI命令用于開啟一個事務(wù)隔节,它總是返回OK鹅经。 MULTI執(zhí)行之后,客戶端可以繼續(xù)向服務(wù)器發(fā)送任意多條命令官帘,這些命令不會立即被執(zhí)行瞬雹,而是被放到一個隊列中,當(dāng)EXEC命令被調(diào)用時刽虹,所有隊列中的命令才會被執(zhí)行。

EXEC:執(zhí)行所有事務(wù)塊內(nèi)的命令呢诬。返回事務(wù)塊內(nèi)所有命令的返回值涌哲,按命令執(zhí)行的先后順序排列儒溉。 當(dāng)操作被打斷時筹裕,返回空值 nil 。

通過調(diào)用DISCARD灭红,客戶端可以清空事務(wù)隊列狗唉,并放棄執(zhí)行事務(wù)初烘, 并且客戶端會從事務(wù)狀態(tài)中退出。

UNWATCH命令可以取消watch對所有key的監(jiān)控分俯。

事務(wù)管理(ACID)概述

原子性(Atomicity)

原子性是指事務(wù)是一個不可分割的工作單位肾筐,事務(wù)中的操作要么都發(fā)生,要么都不發(fā)生缸剪。

一致性(Consistency)

事務(wù)前后數(shù)據(jù)的完整性必須保持一致吗铐。

隔離性(Isolation)

多個事務(wù)并發(fā)執(zhí)行時,一個事務(wù)的執(zhí)行不應(yīng)影響其他事務(wù)的執(zhí)行

持久性(Durability)

持久性是指一個事務(wù)一旦被提交杏节,它對數(shù)據(jù)庫中數(shù)據(jù)的改變就是永久性的唬渗,接下來即使數(shù)據(jù)庫發(fā)生故障也不應(yīng)該對其有任何影響

Redis的事務(wù)總是具有ACID中的一致性和隔離性典阵,其他特性是不支持的。當(dāng)服務(wù)器運行在AOF持久化模式下镊逝,并且appendfsync選項的值為always時壮啊,事務(wù)也具有耐久性。

Redis事務(wù)支持隔離性嗎

Redis 是單進程程序撑蒜,并且它保證在執(zhí)行事務(wù)時他巨,不會對事務(wù)進行中斷,事務(wù)可以運行直到執(zhí)行完所有事務(wù)隊列中的命令為止减江。因此染突,Redis 的事務(wù)是總是帶有隔離性的

Redis事務(wù)保證原子性嗎辈灼,支持回滾嗎

Redis中份企,單條命令是原子性執(zhí)行的,但事務(wù)不保證原子性巡莹,且沒有回滾司志。事務(wù)中任意命令執(zhí)行失敗,其余的命令仍會被執(zhí)行降宅。

Redis事務(wù)其他實現(xiàn)

基于Lua腳本骂远,Redis可以保證腳本內(nèi)的命令一次性、按順序地執(zhí)行腰根,

其同時也不提供事務(wù)運行錯誤的回滾激才,執(zhí)行過程中如果部分命令運行錯誤,剩下的命令還是會繼續(xù)運行完

基于中間標記變量额嘿,通過另外的標記變量來標識事務(wù)是否執(zhí)行完成瘸恼,讀取數(shù)據(jù)時先讀取該標記變量判斷是否事務(wù)執(zhí)行完成。但這樣會需要額外寫代碼實現(xiàn)册养,比較繁瑣

集群方案

哨兵模式

哨兵的介紹

sentinel东帅,中文名是哨兵。哨兵是 redis 集群機構(gòu)中非常重要的一個組件球拦,主要有以下功能:

集群監(jiān)控:負責(zé)監(jiān)控 redis master 和 slave 進程是否正常工作靠闭。

消息通知:如果某個 redis 實例有故障,那么哨兵負責(zé)發(fā)送消息作為報警通知給管理員坎炼。

故障轉(zhuǎn)移:如果 master node 掛掉了愧膀,會自動轉(zhuǎn)移到 slave node 上。

配置中心:如果故障轉(zhuǎn)移發(fā)生了点弯,通知 client 客戶端新的 master 地址扇调。

哨兵用于實現(xiàn) redis 集群的高可用,本身也是分布式的抢肛,作為一個哨兵集群去運行狼钮,互相協(xié)同工作碳柱。

故障轉(zhuǎn)移時,判斷一個 master node 是否宕機了熬芜,需要大部分的哨兵都同意才行莲镣,涉及到了分布式選舉的問題。

即使部分哨兵節(jié)點掛掉了涎拉,哨兵集群還是能正常工作的瑞侮,因為如果一個作為高可用機制重要組成部分的故障轉(zhuǎn)移系統(tǒng)本身是單點的,那就很坑爹了鼓拧。

哨兵的核心知識

哨兵至少需要 3 個實例半火,來保證自己的健壯性。

哨兵 + redis 主從的部署架構(gòu)季俩,是不保證數(shù)據(jù)零丟失的钮糖,只能保證 redis 集群的高可用性。

對于哨兵 + redis 主從這種復(fù)雜的部署架構(gòu)酌住,盡量在測試環(huán)境和生產(chǎn)環(huán)境店归,都進行充足的測試和演練。

官方Redis Cluster 方案(服務(wù)端路由查詢)

redis 集群模式的工作原理能說一下么酪我?在集群模式下消痛,redis 的 key 是如何尋址的?分布式尋址都有哪些算法都哭?了解一致性 hash 算法嗎秩伞?

簡介

Redis Cluster是一種服務(wù)端Sharding技術(shù),3.0版本開始正式提供质涛。Redis Cluster并沒有使用一致性hash稠歉,而是采用slot(槽)的概念,一共分成16384個槽汇陆。將請求發(fā)送到任意節(jié)點,接收到請求的節(jié)點會將查詢請求發(fā)送到正確的節(jié)點上執(zhí)行

方案說明

通過哈希的方式带饱,將數(shù)據(jù)分片毡代,每個節(jié)點均分存儲一定哈希槽(哈希值)區(qū)間的數(shù)據(jù),默認分配了16384 個槽位

每份數(shù)據(jù)分片會存儲在多個互為主從的多節(jié)點上

數(shù)據(jù)寫入先寫主節(jié)點勺疼,再同步到從節(jié)點(支持配置為阻塞同步)

同一分片多個節(jié)點間的數(shù)據(jù)不保持一致性

讀取數(shù)據(jù)時教寂,當(dāng)客戶端操作的key沒有分配在該節(jié)點上時,redis會返回轉(zhuǎn)向指令执庐,指向正確的節(jié)點

擴容時時需要需要把舊節(jié)點的數(shù)據(jù)遷移一部分到新節(jié)點

在 redis cluster 架構(gòu)下酪耕,每個 redis 要放開兩個端口號,比如一個是 6379轨淌,另外一個就是 加1w 的端口號迂烁,比如 16379看尼。

16379 端口號是用來進行節(jié)點間通信的,也就是 cluster bus 的東西盟步,cluster bus 的通信藏斩,用來進行故障檢測、配置更新却盘、故障轉(zhuǎn)移授權(quán)狰域。cluster bus 用了另外一種二進制的協(xié)議,gossip 協(xié)議黄橘,用于節(jié)點間進行高效的數(shù)據(jù)交換兆览,占用更少的網(wǎng)絡(luò)帶寬和處理時間。

節(jié)點間的內(nèi)部通信機制

基本通信原理

集群元數(shù)據(jù)的維護有兩種方式:集中式塞关、Gossip 協(xié)議抬探。redis cluster 節(jié)點間采用 gossip 協(xié)議進行通信。

分布式尋址算法

hash 算法(大量緩存重建)

一致性 hash 算法(自動緩存遷移)+ 虛擬節(jié)點(自動負載均衡)

redis cluster 的 hash slot 算法

優(yōu)點

無中心架構(gòu)描孟,支持動態(tài)擴容驶睦,對業(yè)務(wù)透明

具備Sentinel的監(jiān)控和自動Failover(故障轉(zhuǎn)移)能力

客戶端不需要連接集群所有節(jié)點,連接集群中任何一個可用節(jié)點即可

高性能匿醒,客戶端直連redis服務(wù)场航,免去了proxy代理的損耗

缺點

運維也很復(fù)雜,數(shù)據(jù)遷移需要人工干預(yù)

只能使用0號數(shù)據(jù)庫

不支持批量操作(pipeline管道操作)

分布式邏輯和存儲模塊耦合等

基于客戶端分配

簡介

Redis Sharding是Redis Cluster出來之前廉羔,業(yè)界普遍使用的多Redis實例集群方法溉痢。其主要思想是采用哈希算法將Redis數(shù)據(jù)的key進行散列,通過hash函數(shù)憋他,特定的key會映射到特定的Redis節(jié)點上孩饼。Java redis客戶端驅(qū)動jedis,支持Redis Sharding功能竹挡,即ShardedJedis以及結(jié)合緩存池的ShardedJedisPool

優(yōu)點

優(yōu)勢在于非常簡單镀娶,服務(wù)端的Redis實例彼此獨立,相互無關(guān)聯(lián)揪罕,每個Redis實例像單服務(wù)器一樣運行梯码,非常容易線性擴展,系統(tǒng)的靈活性很強

缺點

由于sharding處理放到客戶端好啰,規(guī)模進一步擴大時給運維帶來挑戰(zhàn)轩娶。

客戶端sharding不支持動態(tài)增刪節(jié)點。服務(wù)端Redis實例群拓撲結(jié)構(gòu)有變化時框往,每個客戶端都需要更新調(diào)整鳄抒。連接不能共享,當(dāng)應(yīng)用規(guī)模增大時,資源浪費制約優(yōu)化

基于代理服務(wù)器分片

簡介

客戶端發(fā)送請求到一個代理組件许溅,代理解析客戶端的數(shù)據(jù)瓤鼻,并將請求轉(zhuǎn)發(fā)至正確的節(jié)點,最后將結(jié)果回復(fù)給客戶端

特征

透明接入闹司,業(yè)務(wù)程序不用關(guān)心后端Redis實例娱仔,切換成本低

Proxy 的邏輯和存儲的邏輯是隔離的

代理層多了一次轉(zhuǎn)發(fā),性能有所損耗

業(yè)界開源方案

Twtter開源的Twemproxy

豌豆莢開源的Codis

Redis 主從架構(gòu)

單機的 redis游桩,能夠承載的 QPS 大概就在上萬到幾萬不等牲迫。對于緩存來說,一般都是用來支撐讀高并發(fā)的借卧。因此架構(gòu)做成主從(master-slave)架構(gòu)盹憎,一主多從,主負責(zé)寫铐刘,并且將數(shù)據(jù)復(fù)制到其它的 slave 節(jié)點陪每,從節(jié)點負責(zé)讀。所有的讀請求全部走從節(jié)點镰吵。這樣也可以很輕松實現(xiàn)水平擴容檩禾,支撐讀高并發(fā)

redis replication -> 主從架構(gòu) -> 讀寫分離 -> 水平擴容支撐讀高并發(fā)

redis replication 的核心機制

redis 采用異步方式復(fù)制數(shù)據(jù)到 slave 節(jié)點疤祭,不過 redis2.8 開始盼产,slave node 會周期性地確認自己每次復(fù)制的數(shù)據(jù)量;

一個 master node 是可以配置多個 slave node 的勺馆;

slave node 也可以連接其他的 slave node戏售;

slave node 做復(fù)制的時候,不會 block master node 的正常工作草穆;

slave node 在做復(fù)制的時候灌灾,也不會 block 對自己的查詢操作,它會用舊的數(shù)據(jù)集來提供服務(wù)悲柱;但是復(fù)制完成的時候锋喜,需要刪除舊數(shù)據(jù)集,加載新數(shù)據(jù)集豌鸡,這個時候就會暫停對外服務(wù)了跑芳;

slave node 主要用來進行橫向擴容,做讀寫分離直颅,擴容的 slave node 可以提高讀的吞吐量。

注意怀樟,如果采用了主從架構(gòu)功偿,那么建議必須開啟 master node 的持久化,不建議用 slave node 作為 master node 的數(shù)據(jù)熱備,因為那樣的話械荷,如果你關(guān)掉 master 的持久化共耍,可能在 master 宕機重啟的時候數(shù)據(jù)是空的,然后可能一經(jīng)過復(fù)制吨瞎, slave node 的數(shù)據(jù)也丟了痹兜。

另外,master 的各種備份方案颤诀,也需要做字旭。萬一本地的所有文件丟失了,從備份中挑選一份 rdb 去恢復(fù) master崖叫,這樣才能確保啟動的時候遗淳,是有數(shù)據(jù)的,即使采用了后續(xù)講解的高可用機制心傀,slave node 可以自動接管 master node屈暗,但也可能 sentinel 還沒檢測到 master failure,master node 就自動重啟了脂男,還是可能導(dǎo)致上面所有的 slave node 數(shù)據(jù)被清空养叛。

redis 主從復(fù)制的核心原理

當(dāng)啟動一個 slave node 的時候,它會發(fā)送一個 PSYNC 命令給 master node宰翅。

如果這是 slave node 初次連接到 master node弃甥,那么會觸發(fā)一次 full resynchronization 全量復(fù)制。此時 master 會啟動一個后臺線程堕油,開始生成一份 RDB 快照文件潘飘,

同時還會將從客戶端 client 新收到的所有寫命令緩存在內(nèi)存中。RDB 文件生成完畢后掉缺, master 會將這個 RDB 發(fā)送給 slave卜录,slave 會先寫入本地磁盤,然后再從本地磁盤加載到內(nèi)存中眶明,

接著 master 會將內(nèi)存中緩存的寫命令發(fā)送到 slave艰毒,slave 也會同步這些數(shù)據(jù)。

slave node 如果跟 master node 有網(wǎng)絡(luò)故障搜囱,斷開了連接丑瞧,會自動重連,連接之后 master node 僅會復(fù)制給 slave 部分缺少的數(shù)據(jù)蜀肘。

過程原理

當(dāng)從庫和主庫建立MS關(guān)系后绊汹,會向主數(shù)據(jù)庫發(fā)送SYNC命令

主庫接收到SYNC命令后會開始在后臺保存快照(RDB持久化過程),并將期間接收到的寫命令緩存起來

當(dāng)快照完成后扮宠,主Redis會將快照文件和所有緩存的寫命令發(fā)送給從Redis

從Redis接收到后西乖,會載入快照文件并且執(zhí)行收到的緩存的命令

之后,主Redis每當(dāng)接收到寫命令時就會將命令發(fā)送從Redis,從而保證數(shù)據(jù)的一致

缺點

所有的slave節(jié)點數(shù)據(jù)的復(fù)制和同步都由master節(jié)點來處理获雕,會照成master節(jié)點壓力太大薄腻,使用主從從結(jié)構(gòu)來解決

Redis集群的主從復(fù)制模型是怎樣的?

為了使在部分節(jié)點失敗或者大部分節(jié)點無法通信的情況下集群仍然可用届案,所以集群使用了主從復(fù)制模型庵楷,每個節(jié)點都會有N-1個復(fù)制品

生產(chǎn)環(huán)境中的 redis 是怎么部署的?

redis cluster楣颠,10 臺機器尽纽,5 臺機器部署了 redis 主實例,另外 5 臺機器部署了 redis 的從實例球碉,每個主實例掛了一個從實例蜓斧,5 個節(jié)點對外提供讀寫服務(wù),每個節(jié)點的讀寫高峰qps可能可以達到每秒 5 萬睁冬,5 臺機器最多是 25 萬讀寫請求/s挎春。

機器是什么配置?32G 內(nèi)存+ 8 核 CPU + 1T 磁盤豆拨,但是分配給 redis 進程的是10g內(nèi)存直奋,一般線上生產(chǎn)環(huán)境,redis 的內(nèi)存盡量不要超過 10g施禾,超過 10g 可能會有問題脚线。

5 臺機器對外提供讀寫,一共有 50g 內(nèi)存弥搞。

因為每個主實例都掛了一個從實例邮绿,所以是高可用的,任何一個主實例宕機攀例,都會自動故障遷移船逮,redis 從實例會自動變成主實例繼續(xù)提供讀寫服務(wù)。

你往內(nèi)存里寫的是什么數(shù)據(jù)粤铭?每條數(shù)據(jù)的大小是多少挖胃?商品數(shù)據(jù),每條數(shù)據(jù)是 10kb梆惯。100 條數(shù)據(jù)是 1mb酱鸭,10 萬條數(shù)據(jù)是 1g。常駐內(nèi)存的是 200 萬條商品數(shù)據(jù)垛吗,占用內(nèi)存是 20g凹髓,僅僅不到總內(nèi)存的 50%。目前高峰期每秒就是 3500 左右的請求量怯屉。

其實大型的公司扁誓,會有基礎(chǔ)架構(gòu)的 team 負責(zé)緩存集群的運維防泵。

說說Redis哈希槽的概念?

Redis集群沒有使用一致性hash,而是引入了哈希槽的概念蝗敢,Redis集群有16384個哈希槽,每個key通過CRC16校驗后對16384取模來決定放置哪個槽足删,集群的每個節(jié)點負責(zé)一部分hash槽寿谴。

Redis集群會有寫操作丟失嗎?為什么失受?

Redis并不能保證數(shù)據(jù)的強一致性讶泰,這意味這在實際中集群在特定的條件下可能會丟失寫操作。

Redis集群之間是如何復(fù)制的拂到?

異步復(fù)制

Redis集群最大節(jié)點個數(shù)是多少痪署?

16384個

Redis集群如何選擇數(shù)據(jù)庫?

Redis集群目前無法做數(shù)據(jù)庫選擇兄旬,默認在0數(shù)據(jù)庫狼犯。

分區(qū)

Redis是單線程的,如何提高多核CPU的利用率领铐?

可以在同一個服務(wù)器部署多個Redis的實例,并把他們當(dāng)作不同的服務(wù)器來使用,在某些時候呢袱,無論如何一個服務(wù)器是不夠的暴氏, 所以,如果你想使用多個CPU音诈,你可以考慮一下分片(shard)幻碱。

為什么要做Redis分區(qū)?

分區(qū)可以讓Redis管理更大的內(nèi)存细溅,Redis將可以使用所有機器的內(nèi)存褥傍。如果沒有分區(qū),你最多只能使用一臺機器的內(nèi)存谒兄。分區(qū)使Redis的計算能力通過簡單地增加計算機得到成倍提升摔桦,Redis的網(wǎng)絡(luò)帶寬也會隨著計算機和網(wǎng)卡的增加而成倍增長。

你知道有哪些Redis分區(qū)實現(xiàn)方案承疲?

客戶端分區(qū)就是在客戶端就已經(jīng)決定數(shù)據(jù)會被存儲到哪個redis節(jié)點或者從哪個redis節(jié)點讀取邻耕。大多數(shù)客戶端已經(jīng)實現(xiàn)了客戶端分區(qū)。

代理分區(qū) 意味著客戶端將請求發(fā)送給代理燕鸽,然后代理決定去哪個節(jié)點寫數(shù)據(jù)或者讀數(shù)據(jù)兄世。代理根據(jù)分區(qū)規(guī)則決定請求哪些Redis實例,然后根據(jù)Redis的響應(yīng)結(jié)果返回給客戶端啊研。redis和memcached的一種代理實現(xiàn)就是Twemproxy

查詢路由(Query routing) 的意思是客戶端隨機地請求任意一個redis實例御滩,然后由Redis將請求轉(zhuǎn)發(fā)給正確的Redis節(jié)點鸥拧。Redis Cluster實現(xiàn)了一種混合形式的查詢路由,但并不是直接將請求從一個redis節(jié)點轉(zhuǎn)發(fā)到另一個redis節(jié)點削解,而是在客戶端的幫助下直接redirected到正確的redis節(jié)點富弦。

Redis分區(qū)有什么缺點?

涉及多個key的操作通常不會被支持氛驮。例如你不能對兩個集合求交集腕柜,因為他們可能被存儲到不同的Redis實例(實際上這種情況也有辦法,但是不能直接使用交集指令)矫废。

同時操作多個key,則不能使用Redis事務(wù).

分區(qū)使用的粒度是key盏缤,不能使用一個非常長的排序key存儲一個數(shù)據(jù)集(The partitioning granularity is the key, so it is not possible to shard a dataset with a single huge key like a very big sorted set)

當(dāng)使用分區(qū)的時候,數(shù)據(jù)處理會非常復(fù)雜蓖扑,例如為了備份你必須從不同的Redis實例和主機同時收集RDB / AOF文件唉铜。

分區(qū)時動態(tài)擴容或縮容可能非常復(fù)雜。Redis集群在運行時增加或者刪除Redis節(jié)點律杠,能做到最大程度對用戶透明地數(shù)據(jù)再平衡潭流,但其他一些客戶端分區(qū)或者代理分區(qū)方法則不支持這種特性。然而俩功,有一種預(yù)分片的技術(shù)也可以較好的解決這個問題幻枉。

分布式問題

Redis實現(xiàn)分布式鎖

Redis為單進程單線程模式,采用隊列模式將并發(fā)訪問變成串行訪問诡蜓,且多客戶端對Redis的連接并不存在競爭關(guān)系Redis中可以使用SETNX命令實現(xiàn)分布式鎖熬甫。

當(dāng)且僅當(dāng) key 不存在,將 key 的值設(shè)為 value蔓罚。 若給定的 key 已經(jīng)存在椿肩,則 SETNX 不做任何動作

SETNX 是『SET if Not eXists』(如果不存在,則 SET)的簡寫豺谈。

返回值:設(shè)置成功郑象,返回 1 。設(shè)置失敗茬末,返回 0 厂榛。

使用SETNX完成同步鎖的流程及事項如下:

使用SETNX命令獲取鎖,若返回0(key已存在丽惭,鎖已存在)則獲取失敗击奶,反之獲取成功

為了防止獲取鎖后程序出現(xiàn)異常,導(dǎo)致其他線程/進程調(diào)用SETNX命令總是返回0而進入死鎖狀態(tài)责掏,需要為該key設(shè)置一個“合理”的過期時間

釋放鎖柜砾,使用DEL命令將鎖數(shù)據(jù)刪除

如何解決 Redis 的并發(fā)競爭 Key 問題

所謂 Redis 的并發(fā)競爭 Key 的問題也就是多個系統(tǒng)同時對一個 key 進行操作,但是最后執(zhí)行的順序和我們期望的順序不同换衬,這樣也就導(dǎo)致了結(jié)果的不同痰驱!

推薦一種方案:分布式鎖(zookeeper 和 redis 都可以實現(xiàn)分布式鎖)证芭。(如果不存在 Redis 的并發(fā)競爭 Key 問題,不要使用分布式鎖担映,這樣會影響性能)

基于zookeeper臨時有序節(jié)點可以實現(xiàn)的分布式鎖废士。大致思想為:每個客戶端對某個方法加鎖時,在zookeeper上的與該方法對應(yīng)的指定節(jié)點的目錄下另萤,生成一個唯一的瞬時有序節(jié)點湃密。 判斷是否獲取鎖的方式很簡單,只需要判斷有序節(jié)點中序號最小的一個四敞。 當(dāng)釋放鎖的時候,只需將這個瞬時節(jié)點刪除即可拔妥。同時忿危,其可以避免服務(wù)宕機導(dǎo)致的鎖無法釋放,而產(chǎn)生的死鎖問題没龙。完成業(yè)務(wù)流程后铺厨,刪除對應(yīng)的子節(jié)點釋放鎖。

在實踐中硬纤,當(dāng)然是從以可靠性為主解滓。所以首推Zookeeper。

參考:http://www.reibang.com/p/8bddd381de06

分布式Redis是前期做還是后期規(guī)模上來了再做好筝家?為什么洼裤?

既然Redis是如此的輕量(單實例只使用1M內(nèi)存),為防止以后的擴容溪王,最好的辦法就是一開始就啟動較多實例腮鞍。即便你只有一臺服務(wù)器,你也可以一開始就讓Redis以分布式的方式運行莹菱,使用分區(qū)移国,在同一臺服務(wù)器上啟動多個實例。

一開始就多設(shè)置幾個Redis實例道伟,例如32或者64個實例迹缀,對大多數(shù)用戶來說這操作起來可能比較麻煩,但是從長久來看做這點犧牲是值得的蜜徽。

這樣的話祝懂,當(dāng)你的數(shù)據(jù)不斷增長,需要更多的Redis服務(wù)器時娜汁,你需要做的就是僅僅將Redis實例從一臺服務(wù)遷移到另外一臺服務(wù)器而已(而不用考慮重新分區(qū)的問題)嫂易。一旦你添加了另一臺服務(wù)器,你需要將你一半的Redis實例從第一臺機器遷移到第二臺機器掐禁。

什么是 RedLock

Redis 官方站提出了一種權(quán)威的基于 Redis 實現(xiàn)分布式鎖的方式名叫 Redlock怜械,此種方式比原先的單節(jié)點的方法更安全颅和。它可以保證以下特性:

安全特性:互斥訪問,即永遠只有一個 client 能拿到鎖

避免死鎖:最終 client 都可能拿到鎖缕允,不會出現(xiàn)死鎖的情況峡扩,即使原本鎖住某資源的 client crash 了或者出現(xiàn)了網(wǎng)絡(luò)分區(qū)

容錯性:只要大部分 Redis 節(jié)點存活就可以正常提供服務(wù)

緩存異常

緩存雪崩

緩存雪崩是指緩存同一時間大面積的失效,所以障本,后面的請求都會落到數(shù)據(jù)庫上教届,造成數(shù)據(jù)庫短時間內(nèi)承受大量請求而崩掉。

解決方案

緩存數(shù)據(jù)的過期時間設(shè)置隨機驾霜,防止同一時間大量數(shù)據(jù)過期現(xiàn)象發(fā)生案训。

一般并發(fā)量不是特別多的時候,使用最多的解決方案是加鎖排隊粪糙。

給每一個緩存數(shù)據(jù)增加相應(yīng)的緩存標記强霎,記錄緩存的是否失效,如果緩存標記失效蓉冈,則更新數(shù)據(jù)緩存城舞。

緩存穿透

緩存穿透是指緩存和數(shù)據(jù)庫中都沒有的數(shù)據(jù),導(dǎo)致所有的請求都落到數(shù)據(jù)庫上寞酿,造成數(shù)據(jù)庫短時間內(nèi)承受大量請求而崩掉家夺。

解決方案

接口層增加校驗,如用戶鑒權(quán)校驗伐弹,id做基礎(chǔ)校驗拉馋,id<=0的直接攔截;

從緩存取不到的數(shù)據(jù)掸茅,在數(shù)據(jù)庫中也沒有取到椅邓,這時也可以將key-value對寫為key-null,緩存有效時間可以設(shè)置短點昧狮,如30秒(設(shè)置太長會導(dǎo)致正常情況也沒法使用)景馁。這樣可以防止攻擊用戶反復(fù)用同一個id暴力攻擊

采用布隆過濾器,將所有可能存在的數(shù)據(jù)哈希到一個足夠大的 bitmap 中逗鸣,一個一定不存在的數(shù)據(jù)會被這個 bitmap 攔截掉合住,從而避免了對底層存儲系統(tǒng)的查詢壓力

附加

對于空間的利用到達了一種極致,那就是Bitmap和布隆過濾器(Bloom Filter)撒璧。

Bitmap: 典型的就是哈希表

缺點是透葛,Bitmap對于每個元素只能記錄1bit信息,如果還想完成額外的功能卿樱,恐怕只能靠犧牲更多的空間僚害、時間來完成了。

布隆過濾器(推薦)

就是引入了k(k>1)k(k>1)個相互獨立的哈希函數(shù)繁调,保證在給定的空間萨蚕、誤判率下靶草,完成元素判重的過程。

它的優(yōu)點是空間效率和查詢時間都遠遠超過一般的算法岳遥,缺點是有一定的誤識別率和刪除困難奕翔。

Bloom-Filter算法的核心思想就是利用多個不同的Hash函數(shù)來解決“沖突”。

Hash存在一個沖突(碰撞)的問題浩蓉,用同一個Hash得到的兩個URL的值有可能相同派继。為了減少沖突,我們可以多引入幾個Hash捻艳,如果通過其中的一個Hash值我們得出某元素不在集合中驾窟,那么該元素肯定不在集合中。只有在所有的Hash函數(shù)告訴我們該元素在集合中時认轨,才能確定該元素存在于集合中纫普。這便是Bloom-Filter的基本思想。

Bloom-Filter一般用于在大數(shù)據(jù)量的集合中判定某元素是否存在好渠。

緩存擊穿

緩存擊穿是指緩存中沒有但數(shù)據(jù)庫中有的數(shù)據(jù)(一般是緩存時間到期),這時由于并發(fā)用戶特別多节视,同時讀緩存沒讀到數(shù)據(jù)拳锚,又同時去數(shù)據(jù)庫去取數(shù)據(jù),引起數(shù)據(jù)庫壓力瞬間增大寻行,造成過大壓力霍掺。和緩存雪崩不同的是,緩存擊穿指并發(fā)查同一條數(shù)據(jù)拌蜘,緩存雪崩是不同數(shù)據(jù)都過期了杆烁,很多數(shù)據(jù)都查不到從而查數(shù)據(jù)庫。

解決方案

設(shè)置熱點數(shù)據(jù)永遠不過期简卧。

加互斥鎖兔魂,互斥鎖

緩存預(yù)熱

緩存預(yù)熱就是系統(tǒng)上線后,將相關(guān)的緩存數(shù)據(jù)直接加載到緩存系統(tǒng)举娩。這樣就可以避免在用戶請求的時候析校,先查詢數(shù)據(jù)庫,然后再將數(shù)據(jù)緩存的問題铜涉!用戶直接查詢事先被預(yù)熱的緩存數(shù)據(jù)智玻!

解決方案

直接寫個緩存刷新頁面,上線時手工操作一下芙代;

數(shù)據(jù)量不大吊奢,可以在項目啟動的時候自動進行加載;

定時刷新緩存纹烹;

緩存降級

當(dāng)訪問量劇增页滚、服務(wù)出現(xiàn)問題(如響應(yīng)時間慢或不響應(yīng))或非核心服務(wù)影響到核心流程的性能時召边,仍然需要保證服務(wù)還是可用的,即使是有損服務(wù)逻谦。系統(tǒng)可以根據(jù)一些關(guān)鍵數(shù)據(jù)進行自動降級掌实,也可以配置開關(guān)實現(xiàn)人工降級。

緩存降級的最終目的是保證核心服務(wù)可用邦马,即使是有損的贱鼻。而且有些服務(wù)是無法降級的(如加入購物車、結(jié)算)滋将。

在進行降級之前要對系統(tǒng)進行梳理邻悬,看看系統(tǒng)是不是可以丟卒保帥;從而梳理出哪些必須誓死保護随闽,哪些可降級父丰;比如可以參考日志級別設(shè)置預(yù)案:

一般:比如有些服務(wù)偶爾因為網(wǎng)絡(luò)抖動或者服務(wù)正在上線而超時,可以自動降級掘宪;

警告:有些服務(wù)在一段時間內(nèi)成功率有波動(如在95~100%之間)蛾扇,可以自動降級或人工降級,并發(fā)送告警魏滚;

錯誤:比如可用率低于90%镀首,或者數(shù)據(jù)庫連接池被打爆了,或者訪問量突然猛增到系統(tǒng)能承受的最大閥值鼠次,此時可以根據(jù)情況自動降級或者人工降級更哄;

嚴重錯誤:比如因為特殊原因數(shù)據(jù)錯誤了,此時需要緊急人工降級腥寇。

服務(wù)降級的目的成翩,是為了防止Redis服務(wù)故障,導(dǎo)致數(shù)據(jù)庫跟著一起發(fā)生雪崩問題赦役。因此麻敌,對于不重要的緩存數(shù)據(jù),可以采取服務(wù)降級策略扩劝,例如一個比較常見的做法就是庸论,Redis出現(xiàn)問題,不去數(shù)據(jù)庫查詢棒呛,而是直接返回默認值給用戶聂示。

熱點數(shù)據(jù)和冷數(shù)據(jù)

熱點數(shù)據(jù),緩存才有價值

對于冷數(shù)據(jù)而言簇秒,大部分數(shù)據(jù)可能還沒有再次訪問到就已經(jīng)被擠出內(nèi)存鱼喉,不僅占用內(nèi)存,而且價值不大。頻繁修改的數(shù)據(jù)扛禽,看情況考慮使用緩存

對于熱點數(shù)據(jù)锋边,比如我們的某IM產(chǎn)品,生日祝福模塊编曼,當(dāng)天的壽星列表豆巨,緩存以后可能讀取數(shù)十萬次。再舉個例子掐场,某導(dǎo)航產(chǎn)品往扔,我們將導(dǎo)航信息,緩存以后可能讀取數(shù)百萬次熊户。

數(shù)據(jù)更新前至少讀取兩次萍膛,緩存才有意義。這個是最基本的策略嚷堡,如果緩存還沒有起作用就失效了蝗罗,那就沒有太大價值了。

那存不存在蝌戒,修改頻率很高串塑,但是又不得不考慮緩存的場景呢?有北苟!比如拟赊,這個讀取接口對數(shù)據(jù)庫的壓力很大,但是又是熱點數(shù)據(jù)粹淋,這個時候就需要考慮通過緩存手段,減少數(shù)據(jù)庫的壓力瑟慈,比如我們的某助手產(chǎn)品的桃移,點贊數(shù),收藏數(shù)葛碧,分享數(shù)等是非常典型的熱點數(shù)據(jù)借杰,但是又不斷變化,此時就需要將數(shù)據(jù)同步保存到Redis緩存进泼,減少數(shù)據(jù)庫壓力蔗衡。

緩存熱點key

緩存中的一個Key(比如一個促銷商品),在某個時間點過期的時候乳绕,恰好在這個時間點對這個Key有大量的并發(fā)請求過來绞惦,這些請求發(fā)現(xiàn)緩存過期一般都會從后端DB加載數(shù)據(jù)并回設(shè)到緩存,這個時候大并發(fā)的請求可能會瞬間把后端DB壓垮洋措。

解決方案

對緩存查詢加鎖济蝉,如果KEY不存在,就加鎖,然后查DB入緩存王滤,然后解鎖贺嫂;其他進程如果發(fā)現(xiàn)有鎖就等待,然后等解鎖后返回數(shù)據(jù)或者進入DB查詢

常用工具

Redis支持的Java客戶端都有哪些雁乡?官方推薦用哪個第喳?

Redisson、Jedis踱稍、lettuce等等曲饱,官方推薦使用Redisson。

Redis和Redisson有什么關(guān)系寞射?

Redisson是一個高級的分布式協(xié)調(diào)Redis客服端渔工,能幫助用戶在分布式環(huán)境中輕松實現(xiàn)一些Java的對象 (Bloom filter, BitSet, Set, SetMultimap, ScoredSortedSet, SortedSet, Map, ConcurrentMap, List, ListMultimap, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, ReadWriteLock, AtomicLong, CountDownLatch, Publish / Subscribe, HyperLogLog)。

Jedis與Redisson對比有什么優(yōu)缺點桥温?

Jedis是Redis的Java實現(xiàn)的客戶端引矩,其API提供了比較全面的Redis命令的支持;Redisson實現(xiàn)了分布式和可擴展的Java數(shù)據(jù)結(jié)構(gòu)侵浸,和Jedis相比旺韭,功能較為簡單,不支持字符串操作掏觉,不支持排序区端、事務(wù)、管道澳腹、分區(qū)等Redis特性织盼。Redisson的宗旨是促進使用者對Redis的關(guān)注分離,從而讓使用者能夠?qū)⒕Ω械胤旁谔幚順I(yè)務(wù)邏輯上酱塔。

其他問題

Redis與Memcached的區(qū)別

兩者都是非關(guān)系型內(nèi)存鍵值數(shù)據(jù)庫沥邻,現(xiàn)在公司一般都是用 Redis 來實現(xiàn)緩存,而且 Redis 自身也越來越強大了羊娃!Redis 與 Memcached 主要有以下不同:

對比參數(shù)RedisMemcached

類型1. 支持內(nèi)存 2. 非關(guān)系型數(shù)據(jù)庫1. 支持內(nèi)存 2. 鍵值對形式 3. 緩存形式

數(shù)據(jù)存儲類型1. String 2. List 3. Set 4. Hash 5. Sort Set 【俗稱ZSet】1. 文本型 2. 二進制類型

查詢【操作】類型1. 批量操作 2. 事務(wù)支持 3. 每個類型不同的CRUD1.常用的CRUD 2. 少量的其他命令

附加功能1. 發(fā)布/訂閱模式 2. 主從分區(qū) 3. 序列化支持 4. 腳本支持【Lua腳本】1. 多線程服務(wù)支持

網(wǎng)絡(luò)IO模型1. 單線程的多路 IO 復(fù)用模型1. 多線程唐全,非阻塞IO模式

事件庫自封轉(zhuǎn)簡易事件庫AeEvent貴族血統(tǒng)的LibEvent事件庫

持久化支持1. RDB 2. AOF不支持

集群模式原生支持 cluster 模式,可以實現(xiàn)主從復(fù)制蕊玷,讀寫分離沒有原生的集群模式邮利,需要依靠客戶端來實現(xiàn)往集群中分片寫入數(shù)據(jù)

內(nèi)存管理機制在 Redis 中,并不是所有數(shù)據(jù)都一直存儲在內(nèi)存中垃帅,可以將一些很久沒用的 value 交換到磁盤Memcached 的數(shù)據(jù)則會一直在內(nèi)存中延届,Memcached 將內(nèi)存分割成特定長度的塊來存儲數(shù)據(jù),以完全解決內(nèi)存碎片的問題贸诚。但是這種方式會使得內(nèi)存的利用率不高祷愉,例如塊的大小為 128 bytes窗宦,只存儲 100 bytes 的數(shù)據(jù),那么剩下的 28 bytes 就浪費掉了二鳄。

適用場景復(fù)雜數(shù)據(jù)結(jié)構(gòu)赴涵,有持久化,高可用需求订讼,value存儲內(nèi)容較大純key-value髓窜,數(shù)據(jù)量非常大,并發(fā)量非常大的業(yè)務(wù)

(1) memcached所有的值均是簡單的字符串欺殿,redis作為其替代者寄纵,支持更為豐富的數(shù)據(jù)類型

(2) redis的速度比memcached快很多

(3) redis可以持久化其數(shù)據(jù)

如何保證緩存與數(shù)據(jù)庫雙寫時的數(shù)據(jù)一致性?

你只要用緩存脖苏,就可能會涉及到緩存與數(shù)據(jù)庫雙存儲雙寫程拭,你只要是雙寫,就一定會有數(shù)據(jù)一致性的問題棍潘,那么你如何解決一致性問題恃鞋?

一般來說,就是如果你的系統(tǒng)不是嚴格要求緩存+數(shù)據(jù)庫必須一致性的話亦歉,緩存可以稍微的跟數(shù)據(jù)庫偶爾有不一致的情況恤浪,最好不要做這個方案,讀請求和寫請求串行化肴楷,串到一個內(nèi)存隊列里去水由,這樣就可以保證一定不會出現(xiàn)不一致的情況

串行化之后,就會導(dǎo)致系統(tǒng)的吞吐量會大幅度的降低赛蔫,用比正常情況下多幾倍的機器去支撐線上的一個請求屎即。

還有一種方式就是可能會暫時產(chǎn)生不一致的情況洞渤,但是發(fā)生的幾率特別小芯肤,就是先更新數(shù)據(jù)庫贪薪,然后再刪除緩存赁濒。

問題場景描述解決

先寫緩存单雾,再寫數(shù)據(jù)庫歪沃,緩存寫成功竣蹦,數(shù)據(jù)庫寫失敗緩存寫成功筝野,但寫數(shù)據(jù)庫失敗或者響應(yīng)延遲晌姚,則下次讀取(并發(fā)讀)緩存時歇竟,就出現(xiàn)臟讀這個寫緩存的方式挥唠,本身就是錯誤的,需要改為先寫數(shù)據(jù)庫焕议,把舊緩存置為失效宝磨;讀取數(shù)據(jù)的時候,如果緩存不存在,則讀取數(shù)據(jù)庫再寫緩存

先寫數(shù)據(jù)庫唤锉,再寫緩存世囊,數(shù)據(jù)庫寫成功,緩存寫失敗寫數(shù)據(jù)庫成功窿祥,但寫緩存失敗株憾,則下次讀取(并發(fā)讀)緩存時晒衩,則讀不到數(shù)據(jù)緩存使用時嗤瞎,假如讀緩存失敗,先讀數(shù)據(jù)庫听系,再回寫緩存的方式實現(xiàn)

需要緩存異步刷新指數(shù)據(jù)庫操作和寫緩存不在一個操作步驟中贝奇,比如在分布式場景下,無法做到同時寫緩存或需要異步刷新(補救措施)時候確定哪些數(shù)據(jù)適合此類場景靠胜,根據(jù)經(jīng)驗值確定合理的數(shù)據(jù)不一致時間掉瞳,用戶數(shù)據(jù)刷新的時間間隔

Redis常見性能問題和解決方案?

Master最好不要做任何持久化工作髓帽,包括內(nèi)存快照和AOF日志文件菠赚,特別是不要啟用內(nèi)存快照做持久化。

如果數(shù)據(jù)比較關(guān)鍵郑藏,某個Slave開啟AOF備份數(shù)據(jù)衡查,策略為每秒同步一次。

為了主從復(fù)制的速度和連接的穩(wěn)定性必盖,Slave和Master最好在同一個局域網(wǎng)內(nèi)拌牲。

盡量避免在壓力較大的主庫上增加從庫

Master調(diào)用BGREWRITEAOF重寫AOF文件,AOF在重寫的時候會占大量的CPU和內(nèi)存資源歌粥,導(dǎo)致服務(wù)load過高塌忽,出現(xiàn)短暫服務(wù)暫停現(xiàn)象失驶。

為了Master的穩(wěn)定性土居,主從復(fù)制不要用圖狀結(jié)構(gòu),用單向鏈表結(jié)構(gòu)更穩(wěn)定嬉探,即主從關(guān)系為:Master<–Slave1<–Slave2<–Slave3…擦耀,這樣的結(jié)構(gòu)也方便解決單點故障問題,實現(xiàn)Slave對Master的替換涩堤,也即眷蜓,如果Master掛了,可以立馬啟用Slave1做Master胎围,其他不變吁系。

Redis官方為什么不提供Windows版本德召?

因為目前Linux版本已經(jīng)相當(dāng)穩(wěn)定,而且用戶量很大汽纤,無需開發(fā)windows版本上岗,反而會帶來兼容性等問題。

一個字符串類型的值能存儲最大容量是多少冒版?

512M

Redis如何做大量數(shù)據(jù)插入液茎?

Redis2.6開始redis-cli支持一種新的被稱之為pipe mode的新模式用于執(zhí)行大量數(shù)據(jù)插入工作。

假如Redis里面有1億個key辞嗡,其中有10w個key是以某個固定的已知的前綴開頭的捆等,如果將它們?nèi)空页鰜恚?/a>

使用keys指令可以掃出指定模式的key列表。

對方接著追問:如果這個redis正在給線上的業(yè)務(wù)提供服務(wù)续室,那使用keys指令會有什么問題栋烤?

這個時候你要回答redis關(guān)鍵的一個特性:redis的單線程的。keys指令會導(dǎo)致線程阻塞一段時間挺狰,線上服務(wù)會停頓明郭,直到指令執(zhí)行完畢,服務(wù)才能恢復(fù)丰泊。這個時候可以使用scan指令薯定,scan指令可以無阻塞的提取出指定模式的key列表,但是會有一定的重復(fù)概率瞳购,在客戶端做一次去重就可以了话侄,但是整體所花費的時間會比直接用keys指令長。

使用Redis做過異步隊列嗎学赛,是如何實現(xiàn)的

使用list類型保存數(shù)據(jù)信息年堆,rpush生產(chǎn)消息,lpop消費消息盏浇,當(dāng)lpop沒有消息時变丧,可以sleep一段時間,然后再檢查有沒有信息绢掰,如果不想sleep的話痒蓬,可以使用blpop, 在沒有信息的時候,會一直阻塞滴劲,直到信息的到來攻晒。redis可以通過pub/sub主題訂閱模式實現(xiàn)一個生產(chǎn)者,多個消費者哑芹,當(dāng)然也存在一定的缺點,當(dāng)消費者下線時捕透,生產(chǎn)的消息會丟失聪姿。

Redis如何實現(xiàn)延時隊列

使用sortedset碴萧,使用時間戳做score, 消息內(nèi)容作為key,調(diào)用zadd來生產(chǎn)消息,消費者使用zrangbyscore獲取n秒之前的數(shù)據(jù)做輪詢處理末购。

Redis回收進程如何工作的破喻?

一個客戶端運行了新的命令,添加了新的數(shù)據(jù)盟榴。

Redis檢查內(nèi)存使用情況曹质,如果大于maxmemory的限制, 則根據(jù)設(shè)定好的策略進行回收擎场。

一個新的命令被執(zhí)行羽德,等等。

所以我們不斷地穿越內(nèi)存限制的邊界迅办,通過不斷達到邊界然后不斷地回收回到邊界以下宅静。

如果一個命令的結(jié)果導(dǎo)致大量內(nèi)存被使用(例如很大的集合的交集保存到一個新的鍵),不用多久內(nèi)存限制就會被這個內(nèi)存使用量超越站欺。

Redis回收使用的是什么算法姨夹?

LRU算法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市矾策,隨后出現(xiàn)的幾起案子磷账,更是在濱河造成了極大的恐慌,老刑警劉巖贾虽,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逃糟,死亡現(xiàn)場離奇詭異,居然都是意外死亡榄鉴,警方通過查閱死者的電腦和手機履磨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來庆尘,“玉大人剃诅,你說我怎么就攤上這事∈患桑” “怎么了矛辕?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長付魔。 經(jīng)常有香客問我聊品,道長,這世上最難降的妖魔是什么几苍? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任翻屈,我火速辦了婚禮,結(jié)果婚禮上妻坝,老公的妹妹穿的比我還像新娘伸眶。我一直安慰自己惊窖,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布厘贼。 她就那樣靜靜地躺著界酒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪嘴秸。 梳的紋絲不亂的頭發(fā)上毁欣,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天,我揣著相機與錄音岳掐,去河邊找鬼凭疮。 笑死,一個胖子當(dāng)著我的面吹牛岩四,可吹牛的內(nèi)容都是我干的哭尝。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼剖煌,長吁一口氣:“原來是場噩夢啊……” “哼材鹦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起耕姊,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤桶唐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后茉兰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體尤泽,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年规脸,在試婚紗的時候發(fā)現(xiàn)自己被綠了坯约。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡莫鸭,死狀恐怖闹丐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情被因,我是刑警寧澤卿拴,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站梨与,受9級特大地震影響堕花,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜粥鞋,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一缘挽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦壕曼、人聲如沸杠袱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至凿掂,卻和暖如春伴榔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背庄萎。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工踪少, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人糠涛。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓援奢,卻偏偏與公主長得像,于是被迫代替她去往敵國和親忍捡。 傳聞我的和親對象是個殘疾皇子集漾,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,691評論 2 361