簡單來講Redis就是一個數據庫,不過與傳統的數據庫不一樣的是Redis的數據是存在內存中的,因此存寫速度很是快,所以Redis被普遍應用於緩存方向。redis
另外,Redis也常常用來作分佈式鎖。Redis提供了多種數據類型來支持不一樣的業務場景。數據庫
除此以外,Redis支持事務、持久化、LUA腳本、LRU驅動事件、多種集羣方案。緩存
主要從「高性能」和「高併發」這兩點來看待這個問題。安全
假如用戶第一次訪問數據庫中的某些數據。這個過程會比較慢,由於是從硬盤上讀取的。將該用戶訪問的數據存在緩存中,這樣下一次再次訪問這些數據的時候就能夠直接從緩存中獲取了。操做緩存就是直接操做內存,因此速度至關快。若是數據庫中的對應數據改變以後,同步改變緩存中相應的數據便可!bash
直接操做緩存可以承受的請求遠遠大於直接訪問數據庫的,因此咱們能夠考慮把數據庫中的部分數據轉移到緩存中去,這樣用戶的一部分請求會直接到緩存這裏而不用通過數據庫。服務器
緩存分爲本地緩存和分佈式緩存。以Java爲例,使用自帶的map或者guava實現的是本地緩存,最主要的特色是輕量以及快速,生命週期隨着JVM的銷燬而結束。而且在多實例的狀況下,每一個實例都須要各自保存一份緩存,緩存不具備一致性。網絡
使用Redis或者Memcached之類的稱爲分佈式緩存,在多實例的狀況下,各實例公用一份緩存數據,緩存具備一致性。缺點是須要保持Redis或Memcached服務的高可用,整個程序架構上較爲複雜。數據結構
如今公司通常都是用Redis來實現緩存,並且Redis自身也愈來愈強大了!多線程
對於Redis和Memcached我總結了下面四點:架構
(a)、Redis支持豐富的數據類型(支持更復雜的應用場景):Redis不只僅支持簡單的K/V類型的數據,同時還提供list、set、zset、hash等數據結構的存儲。Memcached支持簡單的數據類型String。
(b)、Redis支持數據的持久化:能夠將內存中的數據保持在磁盤中,重啓的時候能夠再次加載使用,而Memcached把數據所有存在內存之中。
(c)、集羣模式:Memcached沒有原生的集羣模式,須要依靠客戶端來實現往集羣中分片寫入數據;可是Redis目前是原生支持Cluster模式的。
(d)、Memcached是多線程:非阻塞IO複用的網絡模式;Redis使用單線程的多路IO複用模式。
對比參數 | Redis | Memcached |
類型 | 一、支持內存 二、非關係型數據庫 |
一、支持內存 二、key-value鍵值對形式 三、緩存系統 |
數據存儲類型 | 一、String 二、List 三、Set 四、Hash 五、ZSet(Sorted Set) |
一、文本型 二、二進制類型【新版增長】 |
查詢【操做】類型 | 一、批量操做 二、事務支持【雖然是假的事務】 三、每一個類型不一樣的CRUD |
一、CRUD 二、少許的其餘命令 |
附加功能 | 一、發佈/訂閱模式 二、主從分區 三、序列化支持 四、腳本支持【Lua腳本】 |
一、多線程服務支持 二、多線程、非阻塞模式 |
網絡IO模型 | 一、單進程模式 | |
事件庫 | 自封裝簡易事件庫AeEvent | 貴族血統LibEvent事件庫 |
持久化支持 | 一、RDB 二、AOF |
不支持 |
(a)、String
經常使用命令:set、get、decr,incr,mget等。
String數據結構是簡單的Key-Value類型,Value其實不只能夠是String,也能夠是數字。常規Key-Vaule緩存應用;常規計數:微博數、粉絲數等。
127.0.0.1:6379> set var1 aaa OK 127.0.0.1:6379> get var1 "aaa" 127.0.0.1:6379> set var2 1 OK 127.0.0.1:6379> get var2 "1" 127.0.0.1:6379> incr var2 (integer) 2 127.0.0.1:6379> get var2 "2" 127.0.0.1:6379> decr var2 (integer) 1 127.0.0.1:6379> get var2 "1" 127.0.0.1:6379> mget var1 var2 1) "aaa" 2) "1" 127.0.0.1:6379>
(b)、Hash
經常使用命令:hget、hset、hmset,hmget,hgetall等。
Hash是一個String類型的Field和Value的映射表,Hash特別適合用於存儲對象。後續操做的時候,你能夠直接僅僅修改這個對象中的某個字段的值。好比咱們能夠Hash數據結構來存儲用戶信息,商品信息等。
127.0.0.1:6379> hset user1 id 1 (integer) 1 127.0.0.1:6379> hset user1 name zhouguowei (integer) 1 127.0.0.1:6379> hset user1 age 30 (integer) 1 127.0.0.1:6379> hset user1 location "Zhengzhou Henan" (integer) 1 127.0.0.1:6379> hget user1 id "1" 127.0.0.1:6379> hget user1 location "Zhengzhou Henan" 127.0.0.1:6379> hgetall user1 1) "id" 2) "1" 3) "name" 4) "zhouguowei" 5) "age" 6) "30" 7) "location" 8) "Zhengzhou Henan" 127.0.0.1:6379> hset user1 name "Function" (integer) 0 127.0.0.1:6379> hgetall user1 1) "id" 2) "1" 3) "name" 4) "Function" 5) "age" 6) "30" 7) "location" 8) "Zhengzhou Henan" 127.0.0.1:6379> hmset user2 id 2 name liuda age 28 location "Beijing" OK 127.0.0.1:6379> hgetall user2 1) "id" 2) "2" 3) "name" 4) "liuda" 5) "age" 6) "28" 7) "location" 8) "Beijing" 127.0.0.1:6379> hmget user2 id name age 1) "2" 2) "liuda" 3) "28" 127.0.0.1:6379>
(c)、List
經常使用命令:lpush、rpush、lpop、rpop、lrange等、
List就是鏈表,Redis List的應用場景很是多,也是Redis最重要的數據結構之一。
好比微博的關注列表,粉絲列表,消息列表等功能均可以用Redis的List結構來實現。
Redis List的實現爲一個雙向鏈表,便可以支持反向查找和遍歷,更方便操做,不過帶來了部分額外的內存開銷。
另外還能夠經過lrange命令,就是從某個元素開始讀取多少個元素,能夠基於List實現分頁查詢。這是很棒的一個功能,基於Redis實現簡單的高性能分頁;能夠作相似微博那種下拉不斷分頁的東西。
127.0.0.1:6379> lpush list1 a (integer) 1 127.0.0.1:6379> lpush list1 b (integer) 2 127.0.0.1:6379> lrange list1 0 1 1) "b" 2) "a" 127.0.0.1:6379> rpush list c (integer) 1 127.0.0.1:6379> lrange list1 0 2 1) "b" 2) "a" 127.0.0.1:6379> rpush list1 c (integer) 3 127.0.0.1:6379> lrange list1 0 2 1) "b" 2) "a" 3) "c" 127.0.0.1:6379> lpop list1 "b" 127.0.0.1:6379> lrange list1 0 1 1) "a" 2) "c" 127.0.0.1:6379> lpop list1 "a" 127.0.0.1:6379> lrange list1 0 1 1) "c" 127.0.0.1:6379>
(d)、Set
經常使用命令:sadd、spop、srem、smembers、sdiff、sdiffstore、sinter,sinterstore、sunion等。
Set對外提供的功能與List相似是一個列表的功能,特殊之處在於Set是能夠自動排重的。當你須要存儲一個列表數據,又不但願出現重複數據時,Set是一個很好的選擇。而且Set提供了判斷某個成員是否在一個Set集合內的重要接口,這個也是List所不能提供的。你能夠基於Set輕易實現交集、並集、差集的操做。
好比:在微博應用中,能夠將一個用戶全部的關注人存在一個集合中,將其全部粉絲存在一個集合。Redis能夠很是方便的時間內如共同關注、共同粉絲、共同喜愛等功能。
127.0.0.1:6379> sadd myset1 hello (integer) 1 127.0.0.1:6379> sadd myset1 world (integer) 1 127.0.0.1:6379> smembers myset1 1) "hello" 2) "world" 127.0.0.1:6379> sadd myset1 hello (integer) 0 127.0.0.1:6379> smembers myset1 1) "hello" 2) "world" 127.0.0.1:6379> sadd myset1 fine (integer) 1 127.0.0.1:6379> smembers myset1 1) "hello" 2) "fine" 3) "world" 127.0.0.1:6379> srem myset1 fine (integer) 1 127.0.0.1:6379> smembers myset1 1) "hello" 2) "world" 127.0.0.1:6379> spop myset1 "hello" 127.0.0.1:6379> smembers myset1 1) "world" 127.0.0.1:6379>
sadd:向名稱爲Key的set中添加元素,同一集合中不能出現相同的元素值。(用法:sadd set集合名稱 元素值)。
srem:刪除名稱爲key的set中的元素。(用法:srem set集合名稱 要刪除的元素值)。
spop:隨機返回並刪除名稱爲key的set中一個元素。(用法:srem set集合名稱)。
127.0.0.1:6379> sadd myset2 two one (integer) 2 127.0.0.1:6379> smembers myset2 1) "two" 2) "one" 127.0.0.1:6379> sadd myset3 three two (integer) 2 127.0.0.1:6379> smembers myset3 1) "two" 2) "three" 127.0.0.1:6379> sdiff myset2 myset3 1) "one" 127.0.0.1:6379>
sdiff:返回全部給定key與第一個key的差集。(用法:sdiff set集合1 set集合2)。
127.0.0.1:6379> smembers myset2 1) "two" 2) "one" 127.0.0.1:6379> smembers myset3 1) "two" 2) "three" 127.0.0.1:6379> sdiffstore myset4 myset2 myset3 (integer) 1 127.0.0.1:6379> smembers myset4 1) "one" 127.0.0.1:6379>
sdiffstore:返回全部給定key與第一個key的差集,並將結果存爲另外一個key。(用法:sdiffstore 差集數據存入的集合 set集合1 set集合2)。
127.0.0.1:6379> smembers myset2 1) "two" 2) "one" 127.0.0.1:6379> smembers myset3 1) "two" 2) "three" 127.0.0.1:6379> sinter myset2 myset3 1) "two" 127.0.0.1:6379>
sinter:返回全部給定key的交集。(用法:sinter set集合1 set集合2)。
127.0.0.1:6379> smembers myset2 1) "two" 2) "one" 127.0.0.1:6379> smembers myset3 1) "two" 2) "three" 127.0.0.1:6379> sinterstore myset5 myset2 myset3 (integer) 1 127.0.0.1:6379> smembers myset5 1) "two" 127.0.0.1:6379>
sinterstore:返回全部給定Set集合的交集,並將結果存爲另外一個set集合。(用法:sinterstore 交集結果集合 set集合1 set集合2)。
127.0.0.1:6379> smembers myset2 1) "two" 2) "one" 127.0.0.1:6379> smembers myset3 1) "two" 2) "three" 127.0.0.1:6379> sunion myset2 myset3 1) "two" 2) "three" 3) "one" 127.0.0.1:6379>
sunion:返回全部給定key的並集。(用法:sunion set集合1 set集合2)。
127.0.0.1:6379> smembers myset2 1) "two" 2) "one" 127.0.0.1:6379> smembers myset3 1) "two" 2) "three" 127.0.0.1:6379> sunionstore myset6 myset2 myset3 (integer) 3 127.0.0.1:6379> smembers myset6 1) "two" 2) "three" 3) "one" 127.0.0.1:6379>
sunionstore:返回全部給定key的並集,並將結果存爲另外一個set集合。(用法:sunionstore 並集結果集合 set集合1 set集合2)。
(e)、Zset(Sorted Set)
經常使用命令:zadd、zrange、zrem、zcard等。
和Set相比,Sorted Set增長了一個權重參數Score,使得集合中的元素可以按照Score進行排序。
舉例:在直播系統中,實時排行信息包含直播間在線用戶列表,各類禮物排行榜,彈幕消息(能夠理解爲按消息緯度的消息排行榜)等信息,適合使用Redis中的Sorted Set結構進行存儲。
127.0.0.1:6379> zadd zset1 1 two (integer) 1 127.0.0.1:6379> zadd zset1 2 one (integer) 1 127.0.0.1:6379> zadd zset1 3 seven (integer) 1 127.0.0.1:6379> zrange zset1 0 -1 1) "two" 2) "one" 3) "seven" 127.0.0.1:6379> zrange zset1 0 -1 withscores 1) "two" 2) "1" 3) "one" 4) "2" 5) "seven" 6) "3" 127.0.0.1:6379>
zadd:向名稱爲key的zset中添加元素member,score用於排序。若是該元素存在,則更新其順序。(用法:zadd 有序集合 順序編號 元素值)。
127.0.0.1:6379> zrange zset1 0 -1 withscores 1) "two" 2) "1" 3) "one" 4) "2" 5) "seven" 6) "3" 127.0.0.1:6379> zrem zset1 one (integer) 1 127.0.0.1:6379> zrange zset1 0 -1 withscores 1) "two" 2) "1" 3) "seven" 4) "3" 127.0.0.1:6379>
zrem:刪除名稱爲key的zset中的元素。(用法:zrem 有序集合 要刪除的元素值)。
127.0.0.1:6379> zrange zset1 0 -1 withscores 1) "two" 2) "1" 3) "seven" 4) "3" 127.0.0.1:6379> zincrby zset1 5 seven "8" 127.0.0.1:6379> zrange zset1 0 -1 withscores 1) "two" 2) "1" 3) "seven" 4) "8" 127.0.0.1:6379>
zincrby:若是在名稱爲key的zset中已經存在元素member,則該元素的score增長increment,不然向該集合中添加該元素,其score的值爲increment.即對元素的順序號進行增長或減小操做。(用法:zincrby 有序集合 increment 指定的元素值)。
127.0.0.1:6379> zrange zset1 0 -1 withscores 1) "two" 2) "1" 3) "seven" 4) "8" 127.0.0.1:6379> zcard zset1 (integer) 2 127.0.0.1:6379>
zcard:返回集合中元素個數。(用法:zcard 有序集合)。
Redis中有個設置過時時間的功能,即對存儲在Redis數據庫中的值能夠設置一個過時時間。做爲一個緩存數據庫,這個是很是實用的。
如咱們通常項目中的Token或者一些登陸信息,尤爲是短信驗證都會有時間限制的,按照傳統的數據庫處理方式,通常都是本身判斷過時,這樣無疑會嚴重影響項目性能。
咱們Set Key的時候,均可以給一個Expire Time,就是過時時間,經過過時時間咱們能夠指定這個Key能夠存活的時間。
若是你設置了一批Key只能存活一個小時,那麼接下來一小時後,Redis是怎麼對這批Key進行刪除的?
答案是:按期刪除+惰性刪除。經過名字大概就能猜出這兩個刪除方式的意思了:
(a)、按期刪除:Redis默認是每一個100ms就隨機抽取一些設置了過時時間的Key,檢查其是否過時,若是過時就刪除。
注意:這裏是隨機抽取的。爲何要隨機抽取呢?你想想假如Redis存了幾十萬個Key,每一個100ms就遍歷全部的設置過時時間的Key的話,就會給CPU帶來很大的負載!
(b)、惰性刪除:按期刪除可能會致使不少過時Key到了時間並無被刪除掉。因此就有了惰性刪除。假如你的過時Key,靠按期刪除沒有被刪除掉,還停留在內存裏,除非你的系統去查一下哪一個Key,纔會被Redis給刪除掉。這就是所謂的惰性刪除,也是夠懶的。
可是僅僅經過設置過時時間仍是有問題的。咱們想一下:若是按期刪除漏掉了不少過時的Key,而後你也沒及時去查,也就沒有走惰性刪除,此時會怎麼樣?若是大量過時Key堆積在內存裏,致使Redis內存塊耗盡了。怎麼解決這個問題呢?
MySQL裏有2000W數據,Redis中只存20W數據,如何保證Redis中的數據都是熱點數據?
Redis配置文件redis.conf中有關注釋,我這裏就不貼了,你們能夠自行查閱或者經過這個網址查看:http://download.redis.io/redis-stable/redis.conf。
Redis提供了6中數據淘汰策略:
(a)、volatile-lru:從已設置過時時間的數據集(sever.db[i].expires)中挑選最近最少使用的數據淘汰。
(b)、volatile-ttl:從已設置過時時間的數據集(sever.db[i].expires)中挑選將要過時的數據淘汰。
(c)、volatile-random:從已設置過時時間的數據集(sever.db[i].expires)中任意選擇數據淘汰。
(d)、allkeys-lru:當內存不足以容納新寫入數據時,在鍵空間中,移除最近最少使用的key(這個是最經常使用的)。
(e)、allkey-random:從數據集(server.db[i].dict)中任意選擇數據淘汰。
(f)、no-enviction:禁止驅逐數據,也就是說當內存不足以容納新寫入數據時,新寫入操做會報錯。這個應該沒有人使用吧!
怎麼保證Redis掛掉以後重啓數據能夠進行恢復?不少時候咱們須要持久化數據也就是內存中的數據寫入到硬盤裏面。
大部分緣由是爲了以後重用數據(好比重啓機器、機器故障以後恢復數據),或者爲了防止系統故障而將數據備份到一個遠程位置。
Redis不一樣於Memcached的重要一點就是,Redis支持持久化,並且支持兩種不一樣的持久化操做。
Redis的一種持久化方式叫快照(anapshotting,RDB),另外一種方式是隻追加文件(append-only file,AOF)。
這兩種方法各有千秋,下面我會詳細講這兩種持久化方法是什麼,怎麼用,如何選擇適合本身的持久化方法。
(a)、快照(snapshotting)持久化(RDB)
Redis能夠經過建立快照來獲取存儲在內存裏面的數據在某個時間點上的副本。
Redis建立快照以後,能夠對快照進行備份,能夠將快照複製到其餘服務器從而建立具備相同數據的服務器副本(Redis主從結構,主要用來提升Redis性能),還能夠將快照留在原地以便重啓服務器的時候使用。
快照持久化是Redis默認採用的持久化方式,在rredis.conf配置文件中默認有此配置:
save 900 1 #在900秒(15分鐘)以後,若是至少有1個key發生變化,Redis就會自動觸發BGSAVE命令建立快照。 save 300 10 #在300秒(5分鐘)以後,若是至少有10個key發生變化,Redis就會自動觸發BGSAVE命令建立快照。 save 60 10000 #在60秒(1分鐘)以後,若是至少有10000個key發生變化,Redis就會自動觸發BGSAVE命令建立快照。
(b)、AOF(append-only file)持久化
與快照持久化相比,AOF持久化的實時性更好,所以已成爲主流的持久化方案。
默認狀況下,Redis沒有開啓AOF(append-only file)方式的持久化,能夠經過appendonly參數開啓:
appendonly yes
開啓AOF持久化後每執行一條會更改Redis中的數據的命令,Redis就會將該命令寫入硬盤中的AOF文件。
AOF文件的保存位置和RDB文件的位置相同,都是經過dir參數配置的,默認的文件名是appendonly.aof。
在Redis的配置文件中存在三種不一樣的AOF持久化方式,它們分別是:
appendfsync always #每次有數據修改發生時都會寫入AOF文件,這樣會嚴重下降Redis的速度 appendfsync everysec #每秒鐘同步一次,顯示地將多個寫命令同步到硬盤 appendfsync no #讓操做系統決定什麼時候進行同步
爲了兼顧數據和寫入性能,用戶能夠考慮appendfsync everysec選項,讓Redis每秒同步一次AOF文件,Redis性能幾乎沒收到任何影響。
而這樣即便出現系統崩潰,用戶最多隻會丟失一秒以內產生的數據。當硬盤忙於執行寫入操做的時候,Redis還會優雅的放慢本身的速度以便適應磁盤的最大寫入速度。
Redis 4.0 開始支持RDB和AOF的混合持久化(默認關閉,能夠經過配置項aof-use-rdb-preamble開啓)。
若是把混合持久化打開,AOF重寫的時候就直接把RDB的內容寫到AOF文件開頭。
這樣作的好處是能夠結合RDB和AOF的優勢,快速加載同時避免丟失過多的數據。
固然缺點也是有的,AOF裏面的RDB部分是壓縮格式,再也不是AOF格式,可讀性較差。
AOF重寫能夠產生一個新的AOF文件,這個新的AOF文件和原有的AOF文件所保存的數據庫狀態同樣,但體積更小。
AOF重寫是一個歧義的名字,該功能是經過讀取數據庫中的鍵值對來實現的,程序無須對現有AOF文件進行任何讀入、分析或者寫入操做。
在執行BGREWRITEAOF命令時,Redis服務器會維護一個AOF重寫緩存區,該緩存區會在子進程建立新AOF文件期間,記錄服務器執行的全部寫命令。
當子進程完成建立新AOF文件的工做以後,服務器會重寫緩存區中的全部內容追加到新AOF文件的末尾,使得新舊兩個AOF文件所保存的數據庫狀態一致。
最後,服務器用新的AOF文件替換舊的AOF文件,以此來完成AOF文件從新操做。
Redis經過MULTI、EXEC、WATCH等命令來實現事務(transaction)功能。
事務提供了一種將多個命令請求打包,而後一次性,按順序地執行多個命令的機制。
而且在事務執行期間,服務器不會中斷事務而改去執行其餘客戶端的命令請求,它會將事務中的全部命令執行完畢,而後纔去處理其餘客戶端的命令請求。
在傳統的關係數據庫中,經常用ACID性質來驗證事務功能的可靠性和安全性。
在Redis中,事務老是具備原子性(Atomicity)、一致性(Consistency)和隔離性(Isolation),而且當Redis運行在某種特定的持久化模式下時,事務也具備持久性(Durability)。
127.0.0.1:6379> multi OK 127.0.0.1:6379> set a aaa QUEUED 127.0.0.1:6379> set b bbb QUEUED 127.0.0.1:6379> set c ccc QUEUED 127.0.0.1:6379> exec 1) OK 2) OK 3) OK 127.0.0.1:6379> keys * 1) "name" 2) "age" 3) "school" 4) "b" 5) "a" 6) "c" 127.0.0.1:6379> multi OK 127.0.0.1:6379> set aa aaa QUEUED 127.0.0.1:6379> set bb bbb QUEUED 127.0.0.1:6379> discard OK 127.0.0.1:6379>
一、緩存雪崩
簡介:緩存同一時間大面積失效,因此,後面的請求都會落到數據庫上,形成數據庫短期內承受大量請求而崩掉。
解決辦法:
(a)、事前:儘可能保證整個Redis集羣的高可用,發現機器宕機儘快補上。選擇合適的內存淘汰策略。
(b)、事中:本地Ehcache + Hystrix限流&降級,避免MySQL崩掉。
(c)、過後:利用Redis持久化機制保存的數據儘快恢復緩存。
二、緩存穿透
簡介:通常是黑客故意去請求緩存中不存在的數據,致使全部的請求都落到數據庫上,形成數據庫短期內承受大量的請求而崩掉。
解決辦法:有不少方法能夠有效地解決緩存穿透問題,最多見的則是採用布隆過濾器,將全部可能存在的數據哈希到一個足夠大的bitmap中。
一個必定不存在的數據會被這個bitmap攔截掉,從而避免了對底層存儲系統的查詢壓力。
另外也有一個更爲簡單粗暴的方法(咱們採用的就是這種),若是一個查詢返回的數據爲空(無論數據不存在,仍是系統故障),咱們任然把這個空結果進行緩存,但它的過時時間會很短,最長不超過五分鐘。
所謂Redis的併發競爭Key的問題也就是多個系統同時對一個Key進行操做,可是最後執行的順序和咱們指望的順序不一樣,這樣也就致使告終果的不一樣。
推薦一種方案:分佈式鎖(Zookeeper和Redis均可以實現分佈式鎖)。若是不存在Redis的併發競爭Key問題,不要使用分佈式鎖,這樣會影響性能。
基於ZooKeeper臨時有序節點能夠實現的分佈式鎖。大體思想爲:每一個客戶端對某個方法加鎖時,在ZooKeeper上的該方法對應的指定節點的目錄下,生成一個惟一的瞬時有序節點。
判斷是否獲取鎖的方式很簡單,只須要判斷有序節點中序號最小的一個。放釋放鎖的時候,只需將這個瞬時節點刪除便可。
同時,其能夠避免服務器宕機致使的鎖沒法釋放,而產生的死鎖問題。完成業務流程後,刪除對應的子節點釋放鎖。
你只要要用緩存,就可能會涉及到緩存與數據庫雙存儲雙寫,你只要是雙寫,就必定會有數據一致性問題,那麼你如何解決一致性問題?
通常來講,就是若是你的系統不是嚴格要求緩存 + 數據庫必須一致性的話,緩存能夠稍微的跟數據庫偶爾有不一致性的狀況。
最好不要作這個方案,讀請求和寫請求串行化,串到一個內存隊列裏去,這樣就能夠保證必定不會出現不一致的狀況。
串行話以後,就會致使系統的吞吐量會大幅度的下降,用比正常狀況下多幾倍的機器去支撐線上的一個請求。