天天看點

java面試準備之資料庫(Mysql+Redis)

文章目錄

    • MYSQL
      • 一、事務
          • (一)事務四大特性
          • (二)事務隔離級别
      • 二、索引
          • (一)概念
          • (二)索引優缺點
          • (三)MyISAM(非聚集索引)和InnoDB(聚集索引)
          • (四)存儲引擎InnoDB和MyISAM的差別
          • (五)B+樹相交于B樹的優點以及缺點
          • (六)索引的使用
      • 三、資料庫三範式
      • 四、SQL語句的優化
    • Redis
      • 一、Redis簡介
          • (一)為什麼要用redis來做緩存
          • (二)為什麼要用 redis 而不用 map/guava 做緩存?
      • 二、redis和memcached的差別
          • (一)對于 redis 和 memcached 的差別。
          • (二)redis的線程模型
      • 三、redis常見資料結構及使用場景分析
      • 四、redis設定過期時間及記憶體淘汰機制
          • (一)redis 設定過期時間
          • (二)redis 記憶體淘汰機制(MySQL裡有2000w資料,Redis中隻存20w的資料,如何保證Redis中的資料都是熱點資料)
      • 五、redis持久化機制
          • (一)快照(snapshotting)持久化(RDB)
          • (二)AOF(append-only file)持久化
      • 六、緩存雪崩和緩存穿透問題解決方案
          • (一)緩存雪崩
          • (二)緩存穿透
      • 八、如何保證緩存與資料庫雙寫時的資料一緻性?

MYSQL

一、事務

事務:事務是由一組SQL語句組成的邏輯處理單元,事務具有4屬性,通常稱為事務的ACID屬性,一個最小的不可再分的工作單元;通常一個事務對應一個完整的業務(例如銀行賬戶轉賬業務,該業務就是一個最小的工作單元)

(一)事務四大特性

通過資料庫的redo log重做日志,來保證事務的持久性與原子性。

通過資料庫的undo log撤銷日志,來保證事務的一緻性

通過鎖機制+MVCC來保證事務隔離性

1.原子性(Actomicity):事務是一個原子操作單元,其對資料的修改,要麼全都執行,要麼全都不執行。(一個事務為一個原子,不可分割,要麼成功,要麼失敗)

2.一緻性(Consistent):在事務開始和完成時,資料都必須保持一緻狀态。這意味着所有相關的資料規則都必須應用于事務的修改,以操持完整性;事務結束時,所有的内部資料結構(如B樹索引或雙向連結清單)也都必須是正确的。(經過一系列改動及異常崩潰,最後的結果是我要的)

強一緻性:讀操作可以立即讀到送出的更新操作。

弱一緻性:送出的更新操作,不一定立即會被讀操作讀到,此種情況會存在一個不一緻視窗,指的是讀操作可以讀到最新值的一 段時間。

最終一緻性:是弱一緻性的特例。事務更新一份資料,最終一緻性保證在沒有其他事務更新同樣的值的話,最終所有的事務都會 讀到之前事務更新的最新值。如果沒有錯誤發生,不一緻視窗的大小依賴于:通信延遲,系統負載等。其他一緻性變體還有:

單調一緻性:如果一個程序已經讀到一個值,那麼後續不會讀到更早的值。

會話一緻性:保證用戶端和伺服器互動的會話過程中,讀操作可以讀到更新操作後的最新值。

3.隔離性(Isolation):資料庫系統提供一定的隔離機制,保證事務在不受外部并發操作影響的“獨立”環境執行。這意味着事務處理過程中的中間狀态對外部是不可見的,反之亦然。(mvcc,隔離級别)

4.持久性(Durable):事務完成之後,它對于資料的修改是永久性的,即使出現系統故障也能夠保持。(一旦送出成功,那麼事務涉及的資料都會持久化,即使系統崩潰,修改的資料也不會丢失)

(二)事務隔離級别

1.未送出讀

讀未送出:一個事務可以讀取到另一個事務未送出的修改。這會帶來髒讀、幻讀、不可重複讀問題。(基本沒用)

總結:該級别防止了一個事務修改資料的同時另一個事務修改(對同一資料,下同),沒防止修改的同時讀(即僅對修改加了共享鎖)。

2.送出讀

讀已送出:一個事務隻能讀取另一個事務已經送出的修改。其避免了髒讀,但仍然存在不可重複讀和幻讀問題。

總結:該級别防止了一個事務修改的同時另一個事務修改,也防止修改的同時讀,但沒防止讀的時候修改(即僅對修改加了排它鎖)。

3.可重複讀(MVCC解決該問題–多版本并發控制機制)

可重複讀:同一個事務中多次讀取相同的資料傳回的結果是一樣的。其避免了髒讀和不可重複讀問題,但幻讀依然存在。

總結:該級别防止了一個事務修改的同時另一個事務修改,也防止修改的同時讀,也防止讀的時候修改,但沒防止讀的時候新增/删除(對影響的資料行的修改、讀加了排它鎖,沒對表進行加鎖,解決幻讀,不一定要對表加鎖,即隔離級别變為4,也可以通過MVCC解決)。

4.可串行化(在每個讀的資料行上加共享鎖)

事務串行執行。避免了以上所有問題(沒有并發)。

兩段式鎖:定義了兩種鎖,讀鎖和寫鎖,隻有讀-讀可以共享,其餘的都阻塞。兩段式鎖指的是加鎖和解鎖不能交替,也就是在一個事務内,解鎖操作之後不能再有任何的加鎖操作,如果嚴格按照這個協定來操作資料,那麼最終的執行一定是可串行化。

總結:表加鎖。

MySQL資料庫(InnoDB引擎)預設使用可重複讀( Repeatable read)

二、索引

(一)概念

索引用于快速找出在某個列中有一特定值的行,不使用索引,MySQL必須從第一條記錄開始讀完整個表,直到找出相關的行,表越大,查詢資料所花費的時間就越多,如果表中查詢的列有一個索引,MySQL能夠快速到達一個位置去搜尋資料檔案,而不必檢視所有資料,那麼将會節省很大一部分時間。

假如一張表有一億條資料 ,需要查找其中某一條資料,按照正常邏輯,一條一條的去比對的話,最壞的情況下需要比對一億次才能得到結果,用大O标記法就是O(n)最壞時間複雜度,這是無法接受的,而且這一億條資料顯然不能一次性讀入記憶體供程式使用,是以,這一億次比對在不經緩存優化的情況下就是一億次IO開銷,以現在磁盤的IO能力和CPU的運算能力,有可能需要幾個月才能得出結果。如果把這張表轉換成平衡樹結構(一棵非常茂盛和節點非常多的樹),假設這棵樹有10層,那麼隻需要10次IO開銷就能查找到所需要的資料,速度以指數級别提升,用大O标記法就是O(log n),n是記錄總樹,底數是樹的分叉數,結果就是樹的層次數。

(二)索引優缺點

優點:

1、所有的MySql列類型(字段類型)都可以被索引,也就是可以給任意字段設定索引

2、大大加快資料的查詢速度

缺點:

 1、建立索引和維護索引要耗費時間,并且随着資料量的增加所耗費的時間也會增加

 2、索引也需要占空間,我們知道資料表中的資料也會有最大上線設定的,如果我們有大量的索引,索引檔案可能會比資料檔案更快達到上線值

 3、當對表中的資料進行增加、删除、修改時,索引也需要動态的維護,降低了資料的維護速度。

(三)MyISAM(非聚集索引)和InnoDB(聚集索引)

MyISAM引擎使用B+Tree作為索引結構,葉節點的data域存放的是資料記錄的位址,即:MyISAM索引檔案和資料檔案是分離的,MyISAM的索引檔案僅僅儲存資料記錄的位址。MyISAM中索引檢索的算法為首先按照B+Tree搜尋算法搜尋索引,如果指定的Key存在,則取出其data域的值,然後以data域的值為位址,讀取相應資料記錄。MyISAM的索引方式也叫做“非聚集”的。

InnoDB引擎也使用B+Tree作為索引結構,但是InnoDB的資料檔案本身就是索引檔案,葉節點data域儲存了完整的資料記錄。這個索引的key是資料表的主鍵,是以InnoDB表資料檔案本身就是主索引。這種索引叫做“聚焦索引”。InnoDB的輔助索引的data域存儲相應記錄主鍵的值而不是位址。換句話說,InnoDB的所有輔助索引都引用主鍵作為data域。聚集索引這種實作方式使得按主鍵的搜尋十分高效,但是輔助索引搜尋需要檢索兩遍索引:首先檢索輔助索引獲得主鍵,然後用主鍵到主索引中檢索獲得記錄。InnoDB的索引實作後,不建議使用過長的字段作為主鍵,因為所有輔助索引都引用主索引,過長的主索引會令輔助索引變得過大。在Innodb中也不建議使用非單調的字段作為主鍵,因為InnoDB資料檔案本身是一顆B+Tree,非單調的主鍵會造成在插入新記錄時資料檔案為了維持B+Tree的特性而頻繁的分裂調整,十分低效,建議使用自增字段作為主鍵。

(四)存儲引擎InnoDB和MyISAM的差別

1.InnoDB支援事務,MyISAM不支援。

2.MyISAM适合查詢以及插入為主的應用,InnoDB适合頻繁修改以及涉及到安全性較高的應用。

3.InnoDB支援外鍵,MyISAM不支援。

4.從MySQL5.5.5以後,InnoDB是預設引擎。

5.MyISAM支援全文類型索引,而InnoDB不支援全文索引。

6.InnoDB中不儲存表的總行數,select count() from table時,InnoDB需要掃描整個表計算有多少行,但MyISAM隻需簡單讀出儲存好的總行數即可。注:當count()語句包含where條件時MyISAM也需掃描整個表。

7.對于自增長的字段,InnoDB中必須包含隻有該字段的索引,但是在MyISAM表中可以和其他字段一起建立聯合索引。

8.清空整個表時,InnoDB是一行一行的删除,效率非常慢。MyISAM則會重建表。MyisAM使用delete語句删除後并不會立刻清理磁盤空間,需要定時清理,指令:OPTIMIZE table dept;

9.InnoDB支援行鎖(某些情況下還是鎖整表,如 update table set a=1 where user like ‘%lee%’)

10.Myisam建立表生成三個檔案:.frm 資料表結構 、 .myd 資料檔案 、 .myi 索引檔案,Innodb隻生成一個 .frm檔案,資料存放在ibdata1.log

11.現在一般都選用InnoDB,主要是MyISAM的全表鎖,讀寫串行問題,并發效率鎖表,效率低,MyISAM對于讀寫密集型應用一般是不會去選用的。

12.應用場景:

MyISAM不支援事務處理等進階功能,但它提供高速存儲和檢索,以及全文搜尋能力。如果應用中需要執行大量的SELECT查詢,那麼MyISAM是更好的選擇。

InnoDB用于需要事務處理的應用程式,包括ACID事務支援。如果應用中需要執行大量的INSERT或UPDATE操作,則應該使用InnoDB,這樣可以提高多使用者并發操作的性能。

下面的表總結了何時使用聚集索引或非聚集索引(很重要)。

動作描述 使用聚集索引 使用非聚集索引

列經常被分組排序 應 應

傳回某範圍内的資料 應 不應

一個或極少不同值 不應 不應

小數目的不同值 應 不應

大數目的不同值 不應 應

頻繁更新的列 不應 應

外鍵列 應 應

主鍵列 應 應

頻繁修改索引列 不應 應

(五)B+樹相交于B樹的優點以及缺點

優點:

1.層級更低,IO 次數更少

2.每次都需要查詢到葉子節點,查詢性能穩定

3.葉子節點形成有序連結清單,範圍查詢友善

缺點:B+樹最大的性能問題是會産生大量的随機IO,随着新資料的插入,葉子節點會慢慢分裂,邏輯上連續的葉子節點在實體上往往不連續,甚至分離的很遠,但做範圍查詢時,會産生大量讀随機IO。對于大量的随機寫也一樣,舉一個插入key跨度很大的例子,如7->1000->3->2000 … 新插入的資料存儲在磁盤上相隔很遠,會産生大量的随機寫IO.

(六)索引的使用

何時使用索引

MySQL每次查詢隻使用一個索引。與其說是“資料庫查詢隻能用到一個索引”,倒不如說,和全表掃描比起來,去分析兩個索引B+樹更加耗費時間。是以where A=a and B=b這種查詢使用(A,B)的組合索引最佳,B+樹根據(A,B)來排序。

(1)主鍵,unique字段;

(2)和其他表做連接配接的字段需要加索引;

(3)在where裡使用>,≥,=,<,≤,is null和between等字段;

(4)使用不以通配符開始的like,where A like ‘China%’;

(5)聚集函數MIN(),MAX()中的字段;

(6)order by和group by字段;

何時不使用索引

(1)表記錄太少;

(2)資料重複且分布平均的字段(隻有很少資料值的列);

(3)經常插入、删除、修改的表要減少索引;

(4)text,image等類型不應該建立索引,這些列的資料量大(假如text前10個字元唯一,也可以對text前10個字元建立索引);

(5)MySQL能估計出全表掃描比使用索引更快時,不使用索引;

索引何時失效

(1)組合索引未使用最左字首,例如組合索引(A,B),where B=b不會使用索引;

(2)like未使用最左字首,where A like ‘%China’;

(3)搜尋一個索引而在另一個索引上做order by,where A=a order by B,隻使用A上的索引,因為查詢隻使用一個索引 ;

(4)or會使索引失效。如果查詢字段相同,也可以使用索引。例如where A=a1 or A=a2(生效),where A=a or B=b(失效)

(5)如果列類型是字元串,要使用引号。例如where A=‘China’,否則索引失效(會進行類型轉換);

(6)在索引列上的操作,函數(upper()等)、or、!=(<>)、not in,在 where 子句中對字段進行 null 值判斷,使用!=或<>操作符等;

三、資料庫三範式

第一範式:1NF是對屬性的原子性限制,要求屬性具有原子性,不可再分解;

第二範式:2NF是對記錄的惟一性限制,要求記錄有惟一辨別,即實體的惟一性;

第三範式:3NF是對字段備援性的限制,即任何字段不能由其他字段派生出來,它要求字段沒有備援。。

範式化設計優缺點:

優點:

可以盡量得減少資料備援,使得更新快,體積小

缺點:對于查詢需要多個表進行關聯,減少寫得效率增加讀得效率,更難進行索引優化

反範式化:

優點:可以減少表得關聯,可以更好得進行索引優化

缺點:資料備援以及資料異常,資料得修改需要更多的成本

四、SQL語句的優化

查詢的優化

1、保證在實作功能的基礎上,盡量減少對資料庫的通路次數;

2、通過搜尋參數,盡量減少對表的通路行數,最小化結果集,進而減輕網絡負擔;

3、能夠分開的操作盡量分開處理,提高每次的響應速度;

4、在資料視窗使用SQL時,盡量把使用的索引放在選擇的首列;

5、算法的結構盡量簡單;

6、在查詢時,不要過多地使用通配符如:SELECT * FROM T1 語句,要用到幾列就選擇幾列,如:SELECT COL1,COL2 FROM T1;

7、在可能的情況下盡量限制盡量結果集行數如:SELECT TOP 300 COL1,COL2,COL3 FROM T1,因為某些情況下使用者是不需要那麼多的資料的。

Mysql中的鎖類型

MyISAM支援表鎖,InnoDB支援表鎖和行鎖,預設為行鎖

表級鎖:開銷小,加鎖快,不會出現死鎖。鎖定粒度大,發生鎖沖突的機率最高,并發量最低

行級鎖:開銷大,加鎖慢,會出現死鎖。鎖力度小,發生鎖沖突的機率小,并發度最高

Redis

一、Redis簡介

簡單來說 redis 就是一個資料庫(nosql),不過與傳統資料庫不同的是 redis 的資料是存在記憶體中的,是以讀寫速度非常快,是以 redis 被廣泛應用于緩存方向。另外,redis 也經常用來做分布式鎖。redis 提供了多種資料類型來支援不同的業務場景。除此之外,redis 支援事務 、持久化、LUA腳本、LRU驅動事件、多種叢集方案。

(一)為什麼要用redis來做緩存

可以從高性能、高并發兩個方面來說

高性能:

假如使用者第一次通路資料庫中的某些資料。這個過程會比較慢,因為是從硬碟上讀取的。将該使用者通路的資料存在緩存中,這樣下一次再通路這些資料的時候就可以直接從緩存中擷取了。操作緩存就是直接操作記憶體,是以速度相當快。如果資料庫中的對應資料改變的之後,同步改變緩存中相應的資料即可!

高并發:

直接操作緩存能夠承受的請求是遠遠大于直接通路資料庫的,是以我們可以考慮把資料庫中的部分資料轉移到緩存中去,這樣使用者的一部分請求會直接到緩存這裡而不用經過資料庫。

(二)為什麼要用 redis 而不用 map/guava 做緩存?

緩存分為本地緩存和分布式緩存。以 Java 為例,使用自帶的 map 或者 guava 實作的是本地緩存,最主要的特點是輕量以及快速,生命周期随着 jvm 的銷毀而結束,并且在多執行個體的情況下,每個執行個體都需要各自儲存一份緩存,緩存不具有一緻性。

使用 redis 或 memcached 之類的稱為分布式緩存,在多執行個體的情況下,各執行個體共用一份緩存資料,緩存具有一緻性。缺點是需要保持 redis 或 memcached服務的高可用,整個程式架構上較為複雜。

二、redis和memcached的差別

(一)對于 redis 和 memcached 的差別。

現在公司一般都是用 redis 來實作緩存,而且 redis 自身也越來越強大了!

1.redis支援更豐富的資料類型(支援更複雜的應用場景):Redis不僅僅支援簡單的k/v類型的資料,同時還提供list,set,zset,hash等資料結構的存儲。memcache支援簡單的資料類型,String。

2.Redis支援資料的持久化,可以将記憶體中的資料保持在磁盤中,重新開機的時候可以再次加載進行使用,而Memecache把資料全部存在記憶體之中。

3.叢集模式:memcached沒有原生的叢集模式,需要依靠用戶端來實作往叢集中分片寫入資料;但是 redis 目前是原生支援 cluster 模式的.

4.Memcached是多線程,非阻塞IO複用的網絡模型;Redis使用單線程的多路 IO 複用模型。

(二)redis的線程模型

redis 内部使用檔案事件處理器 file event handler,這個檔案事件處理器是單線程的,是以 redis 才叫做單線程的模型。它采用 IO 多路複用機制同時監聽多個 socket,根據 socket 上的事件來選擇對應的事件處理器進行處理。

檔案事件處理器的結構包含 4 個部分:

1.多個 socket

2.IO 多路複用程式

3.檔案事件分派器

4.事件處理器(連接配接應答處理器、指令請求處理器、指令回複處理器)

多個 socket 可能會并發産生不同的操作,每個操作對應不同的檔案事件,但是 IO 多路複用程式會監聽多個 socket,會将 socket 産生的事件放入隊列中排隊,事件分派器每次從隊列中取出一個事件,把該事件交給對應的事件處理器進行處理。

三、redis常見資料結構及使用場景分析

1.String

常用指令: set,get,decr,incr,mget 等。

String資料結構是簡單的key-value類型,value其實不僅可以是String,也可以是數字。

正常key-value緩存應用;正常計數:微網誌數,粉絲數等。

2.Hash

常用指令: hget,hset,hgetall 等。

hash 是一個 string 類型的 field 和 value 的映射表,hash 特别适合用于存儲對象,後續操作的時候,你可以直接僅僅修改這個對象中的某個字段的值。 比如我們可以 hash 資料結構來存儲使用者資訊,商品資訊等等。

3.List

常用指令: lpush,rpush,lpop,rpop,lrange等

list 就是連結清單,Redis list 的應用場景非常多,也是Redis最重要的資料結構之一,比如微網誌的關注清單,粉絲清單,消息清單等功能都可以用Redis的 list 結構來實作。

Redis list 的實作為一個雙向連結清單,即可以支援反向查找和周遊,更友善操作,不過帶來了部分額外的記憶體開銷。

另外可以通過 lrange 指令,就是從某個元素開始讀取多少個元素,可以基于 list 實作分頁查詢,這個很棒的一個功能,基于 redis 實作簡單的高性能分頁,可以做類似微網誌那種下拉不斷分頁的東西(一頁一頁的往下走),性能高。

4.Set

常用指令:

sadd,spop,smembers,sunion 等

set 對外提供的功能與list類似是一個清單的功能,特殊之處在于 set 是可以自動排重的。

當你需要存儲一個清單資料,又不希望出現重複資料時,set是一個很好的選擇,并且set提供了判斷某個成員是否在一個set集合内的重要接口,這個也是list所不能提供的。可以基于 set 輕易實作交集、并集、差集的操作。

比如:在微網誌應用中,可以将一個使用者所有的關注人存在一個集合中,将其所有粉絲存在一個集合。Redis可以非常友善的實作如共同關注、共同粉絲、共同喜好等功能。這個過程也就是求交集的過程,具體指令如下:

sinterstore key1 key2 key3 将交集存在key1内

5.Sorted Set

常用指令: zadd,zrange,zrem,zcard等

和set相比,sorted set增加了一個權重參數score,使得集合中的元素能夠按score進行有序排列。

舉例: 在直播系統中,實時排行資訊包含直播間線上使用者清單,各種禮物排行榜,彈幕消息(可以了解為按消息次元的消息排行榜)等資訊,适合使用 Redis 中的 Sorted Set 結構進行存儲。

四、redis設定過期時間及記憶體淘汰機制

(一)redis 設定過期時間

Redis中有個設定時間過期的功能,即對存儲在 redis 資料庫中的值可以設定一個過期時間。作為一個緩存資料庫,這是非常實用的。如我們一般項目中的 token 或者一些登入資訊,尤其是短信驗證碼都是有時間限制的,按照傳統的資料庫處理方式,一般都是自己判斷過期,這樣無疑會嚴重影響項目性能。

我們 set key 的時候,都可以給一個 expire time,就是過期時間,通過過期時間我們可以指定這個 key 可以存活的時間。

如果假設你設定了一批 key 隻能存活1個小時,那麼接下來1小時後,redis是怎麼對這批key進行删除的?

定期删除+惰性删除。

通過名字大概就能猜出這兩個删除方式的意思了。

定期删除:redis預設是每隔 100ms 就随機抽取一些設定了過期時間的key,檢查其是否過期,如果過期就删除。注意這裡是随機抽取的。為什麼要随機呢?你想一想假如 redis 存了幾十萬個 key ,每隔100ms就周遊所有的設定過期時間的 key 的話,就會給 CPU 帶來很大的負載!

惰性删除:定期删除可能會導緻很多過期 key 到了時間并沒有被删除掉。是以就有了惰性删除。假如你的過期 key,靠定期删除沒有被删除掉,還停留在記憶體裡,除非你的系統去查一下那個 key,才會被redis給删除掉。這就是所謂的惰性删除,也是夠懶的哈!

但是僅僅通過設定過期時間還是有問題的。我們想一下:如果定期删除漏掉了很多過期 key,然後你也沒及時去查,也就沒走惰性删除,此時會怎麼樣?如果大量過期key堆積在記憶體裡,導緻redis記憶體塊耗盡了。怎麼解決這個問題呢? redis 記憶體淘汰機制。

(二)redis 記憶體淘汰機制(MySQL裡有2000w資料,Redis中隻存20w的資料,如何保證Redis中的資料都是熱點資料)

配置網址: http://download.redis.io/redis-stable/redis.conf

redis 提供 6種資料淘汰政策:

volatile-lru:從已設定過期時間的資料集(server.db[i].expires)中挑選最近最少使用的資料淘汰

volatile-ttl:從已設定過期時間的資料集(server.db[i].expires)中挑選将要過期的資料淘汰

volatile-random:從已設定過期時間的資料集(server.db[i].expires)中任意選擇資料淘汰

allkeys-lru:當記憶體不足以容納新寫入資料時,在鍵空間中,移除最近最少使用的key(這個是最常用的)

allkeys-random:從資料集(server.db[i].dict)中任意選擇資料淘汰

no-eviction:禁止驅逐資料,也就是說當記憶體不足以容納新寫入資料時,新寫入操作會報錯。這個應該沒人使用吧

五、redis持久化機制

Redis不同于Memcached的很重一點就是,Redis支援持久化,而且支援兩種不同的持久化操作。Redis的一種持久化方式叫快照(snapshotting,RDB),另一種方式是隻追加檔案(append-only file,AOF)。這兩種方法各有千秋,下面我會詳細這兩種持久化方法是什麼,怎麼用,如何選擇适合自己的持久化方法。

(一)快照(snapshotting)持久化(RDB)

Redis可以通過建立快照來獲得存儲在記憶體裡面的資料在某個時間點上的副本。Redis建立快照之後,可以對快照進行備份,可以将快照複制到其他伺服器進而建立具有相同資料的伺服器副本(Redis主從結構,主要用來提高Redis性能),還可以将快照留在原地以便重新開機伺服器的時候使用。

快照持久化是Redis預設采用的持久化方式,在redis.conf配置檔案中預設有此下配置:

(二)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 叢集的高可用性,發現機器當機盡快補上。選擇合适的記憶體淘汰政策。

事中:本地ehcache緩存 + hystrix限流&降級,避免MySQL崩掉

事後:利用 redis 持久化機制儲存的資料盡快恢複緩存

(二)緩存穿透

簡介:一般是黑客故意去請求緩存中不存在的資料,導緻所有的請求都落到資料庫上,造成資料庫短時間内承受大量請求而崩掉。

解決辦法: 有很多種方法可以有效地解決緩存穿透問題,最常見的則是采用布隆過濾器,将所有可能存在的資料哈希到一個足夠大的bitmap中,一個一定不存在的資料會被 這個bitmap攔截掉,進而避免了對底層存儲系統的查詢壓力。另外也有一個更為簡單粗暴的方法(我們采用的就是這種),如果一個查詢傳回的資料為空(不管是數 據不存在,還是系統故障),我們仍然把這個空結果進行緩存,但它的過期時間會很短,最長不超過五分鐘。

八、如何保證緩存與資料庫雙寫時的資料一緻性?

你隻要用緩存,就可能會涉及到緩存與資料庫雙存儲雙寫,你隻要是雙寫,就一定會有資料一緻性的問題,那麼你如何解決一緻性問題?

一般來說,就是如果你的系統不是嚴格要求緩存+資料庫必須一緻性的話,緩存可以稍微的跟資料庫偶爾有不一緻的情況,最好不要做這個方案,讀請求和寫請求串行化,串到一個記憶體隊列裡去,這樣就可以保證一定不會出現不一緻的情況

串行化之後,就會導緻系統的吞吐量會大幅度的降低,用比正常情況下多幾倍的機器去支撐線上的一個請求。