1. 程式人生 > 實用技巧 >Java面試——資料庫知識點

Java面試——資料庫知識點

MySQL

1、建

  • 主鍵:資料庫表中對儲存資料物件予以唯一和完整標識的資料列或屬性的組合。一個數據列只能有一個主鍵,且主鍵的取值不能缺失,即不能為空值(Null)。
  • 超鍵:在關係中能唯一標識元組的屬性集稱為關係模式的超鍵。一個屬性可以為作為一個超鍵,多個屬性組合在一起也可以作為一個超鍵。超鍵包含候選鍵和主鍵。
  • 候選鍵:是最小超鍵,即沒有冗餘元素的超鍵。
  • 外來鍵:在一個表中存在的另一個表的主鍵稱此表的外來鍵。

2、事務的四個特性

資料庫事務transanction正確執行的四個基本要素。ACID,原子性(Atomicity)、一致性(Correspondence)、隔離性(Isolation)、永續性(Durability)。

  • 原子性:整個事務中的所有操作,要麼全部完成,要麼全部不完成,不可能停滯在中間某個環節。事務在執行過程中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務從來沒有執行過一樣。
  • 一致性:在事務開始之前和事務結束以後,資料庫的完整性約束沒有被破壞。
  • 隔離性:隔離狀態執行事務,使它們好像是系統在給定時間內執行的唯一操作。如果有兩個事務,執行在相同的時間內,執行 相同的功能,事務的隔離性將確保每一事務在系統中認為只有該事務在使用系統。這種屬性有時稱為序列化,為了防止事務操作間的混淆,必須序列化或序列化請 求,使得在同一時間僅有一個請求用於同一資料。
  • 永續性:在事務完成以後,該事務所對資料庫所作的更改便持久的儲存在資料庫之中,並不會被回滾。

3、檢視的作用

檢視是虛擬的表,與包含資料的表不一樣,檢視只包含使用時動態檢索資料的查詢;不包含任何列或資料。使用檢視可以簡化複雜的sql操作,隱藏具體的細節,保護資料;檢視建立後,可以使用與表相同的方式利用它們。

檢視不能被索引,也不能有關聯的觸發器或預設值,如果檢視本身內有order by 則對檢視再次order by將被覆蓋。

建立檢視:

createviewXXXasXXX;

對於某些檢視比如未使用聯結子查詢分組聚集函式Distinct Union等,是可以對其更新的,對檢視的更新將對基表進行更新;但是檢視主要用於簡化檢索,保護資料,並不用於更新,而且大部分檢視都不可以更新。

drop、delete與truncate的區別

drop直接刪掉表 truncate刪除表中資料,再插入時自增長id又從1開始 delete刪除表中資料,可以加where字句。

  • DELETE語句執行刪除的過程是每次從表中刪除一行,並且同時將該行的刪除操作作為事務記錄在日誌中儲存以便進行進行回滾操作。TRUNCATE TABLE 則一次性地從表中刪除所有的資料並不把單獨的刪除操作記錄記入日誌儲存,刪除行是不能恢復的。並且在刪除的過程中不會啟用與表有關的刪除觸發器。執行速度快。
  • 表和索引所佔空間。當表被TRUNCATE 後,這個表和索引所佔用的空間會恢復到初始大小,而DELETE操作不會減少表或索引所佔用的空間。drop語句將表所佔用的空間全釋放掉。
  • 一般而言,drop > truncate > delete
  • 應用範圍。TRUNCATE 只能對TABLE;DELETE可以是table和view
  • TRUNCATE 和DELETE只刪除資料,而DROP則刪除整個表(結構和資料)。
  • truncate與不帶where的delete :只刪除資料,而不刪除表的結構(定義)drop語句將刪除表的結構被依賴的約束(constrain),觸發器(trigger)索引(index);依賴於該表的儲存過程/函式將被保留,但其狀態會變為:invalid。
  • delete語句為DML(data maintain Language),這個操作會被放到 rollback segment中,事務提交後才生效。如果有相應的 tigger,執行的時候將被觸發。
  • truncate、drop是DLL(data define language),操作立即生效,原資料不放到 rollback segment中,不能回滾
  • 在沒有備份情況下,謹慎使用 drop 與 truncate。要刪除部分資料行採用delete且注意結合where來約束影響範圍。回滾段要足夠大。要刪除表用drop;若想保留表而將表中資料刪除,如果於事務無關,用truncate即可實現。如果和事務有關,或老師想觸發trigger,還是用delete。
  • Truncate table 表名 速度快,而且效率高,因為truncate table 在功能上與不帶 WHERE 子句的 DELETE 語句相同:二者均刪除表中的全部行。但 TRUNCATE TABLE 比 DELETE 速度快,且使用的系統和事務日誌資源少。DELETE 語句每次刪除一行,並在事務日誌中為所刪除的每行記錄一項。TRUNCATE TABLE 通過釋放儲存表資料所用的資料頁來刪除資料,並且只在事務日誌中記錄頁的釋放。
  • TRUNCATE TABLE 刪除表中的所有行,但表結構及其列、約束、索引等保持不變。新行標識所用的計數值重置為該列的種子。如果想保留標識計數值,請改用 DELETE。如果要刪除表定義及其資料,請使用 DROP TABLE 語句。
  • 對於由 FOREIGN KEY 約束引用的表,不能使用 TRUNCATE TABLE,而應使用不帶 WHERE 子句的 DELETE 語句。由於 TRUNCATE TABLE 不記錄在日誌中,所以它不能啟用觸發器。

索引

資料庫索引,是資料庫管理系統中一個排序的資料結構,以協助快速查詢、更新資料庫表中資料。索引的實現通常使用B樹及其變種B+樹。在資料之外,資料庫系統還維護著滿足特定查詢演算法的資料結構,這些資料結構以某種方式引用(指向)資料,這樣就可以在這些資料結構上實現高階查詢演算法。這種資料結構,就是索引。

為表設定索引要付出代價的:

  • 一是增加了資料庫的儲存空間;
  • 二是在插入和修改資料時要花費較多的時間(因為索引也要隨之變動)。

優點:

  • 通過建立唯一性索引,可以保證資料庫表中每一行資料的唯一性;
  • 可以大大加快資料的檢索速度,這也是建立索引的最主要的原因;
  • 可以加速表和表之間的連線,特別是在實現資料的參考完整性方面特別有意義;
  • 在使用分組和排序子句進行資料檢索時,同樣可以顯著減少查詢中分組和排序的時間;
  • 通過使用索引,可以在查詢的過程中,使用優化隱藏器,提高系統的效能。

缺點:

  • 建立索引和維護索引要耗費時間,這種時間隨著資料量的增加而增加;
  • 索引需要佔物理空間,除了資料表佔資料空間之外,每一個索引還要佔一定的物理空間,如果要建立聚簇索引,那麼需要的空間就會更大;
  • 當對錶中的資料進行增加、刪除和修改的時候,索引也要動態的維護,這樣就降低了資料的維護速度。

不具備建立索引列的特點:

  • 對於那些在查詢中很少使用或者參考的列不應該建立索引。這是因為,既然這些列很少使用到,因此有索引或者無索引,並不能提高查詢速度。相反,由於增加了索引,反而降低了系統的維護速度和增大了空間需求。
  • 對於那些只有很少資料值的列也不應該增加索引。這是因為,由於這些列的取值很少,例如人事表的性別列,在查詢的結果中,結果集的資料行佔了表中資料行的很大比例,即需要在表中搜索的資料行的比例很大。增加索引,並不能明顯加快檢索速度。
  • 對於那些定義為text, image和bit資料型別的列不應該增加索引。這是因為,這些列的資料量要麼相當大,要麼取值很少。
  • 當修改效能遠遠大於檢索效能時,不應該建立索引。這是因為,修改效能和檢索效能是互相矛盾的。當增加索引時,會提高檢索效能,但是會降低修改效能。當減少索引時,會提高修改效能,降低檢索效能。因此,當修改效能遠遠大於檢索效能時,不應該建立索引。

資料庫的三種索引:

  • 唯一索引:是不允許其中任何兩行具有相同索引值的索引。當現有資料中存在重複的鍵值時,大多數資料庫不允許將新建立的唯一索引與表一起儲存。資料庫還可能防止新增將在表中建立重複鍵值的新資料。
  • 主鍵索引 :資料庫表經常有一列或列組合,其值唯一標識表中的每一行。該列稱為表的主鍵。 在資料庫關係圖中為表定義主鍵將自動建立主鍵索引,主鍵索引是唯一索引的特定型別。
  • 聚集索引 :在聚集索引中,表中行的物理順序與鍵值的邏輯(索引)順序相同。一個表只能包含一個聚集索引。

連線查詢

外連線 :

包括左向外聯接、右向外聯接或完整外部聯接。

  1. 左連線:left join 或 left outer join

左向外聯接的結果集包括 LEFT OUTER 子句中指定的左表的所有行,而不僅僅是聯接列所匹配的行。如果左表的某行在右表中沒有匹配行,則在相關聯的結果集行中右表的所有選擇列表列均為空值(null)。

select*fromtable1leftjointable2ontable1.id=table2.id
  1. 右連線:right join 或 right outer join

右向外聯接是左向外聯接的反向聯接。將返回右表的所有行。如果右表的某行在左表中沒有匹配行,則將為左表返回空值。

select*fromtable1rightjointable2ontable1.id=table2.id
  1. 完整外部聯接:full join 或 full outer join

完整外部聯接返回左表和右表中的所有行。當某行在另一個表中沒有匹配行時,則另一個表的選擇列表列包含空值。如果表之間有匹配行,則整個結果集行包含基表的資料值。

select*fromtable1fulljointable2ontable1.id=table2.id

內連線:

內聯接是用比較運算子比較要聯接列的值的聯接

1.內連線:join 或 inner join

select*fromtable1jointable2ontable1.id=table2.id

2.等價(與下列執行效果相同)

selecta.*,b.*fromtable1a,table2bwherea.id=b.id
select*fromtable1crossjointable2wheretable1.id=table2.id#注:crossjoin後加條件只能用where,不能用on

交叉連線(完全):

沒有 WHERE 子句的交叉聯接將產生聯接所涉及的表的笛卡爾積。第一個表的行數乘以第二個表的行數等於笛卡爾積結果集的大小。(table1和table2交叉連線產生3*3=9條記錄)

1.交叉連線:cross join (不帶條件where…)

select*fromtable1crossjointable2

2.、等價(與下列執行效果相同)

select*fromtable1,table2

資料庫正規化

第一正規化(1NF):

  • 在任何一個關係資料庫中,第一正規化(1NF)是對關係模式的基本要求,不滿足第一正規化(1NF)的資料庫就不是關係資料庫。
  • 所謂第一正規化(1NF)是指資料庫表的每一列都是不可分割的基本資料項,同一列中不能有多個值,即實體中的某個屬性不能有多個值或者不能有重複的屬性。如果出現重複的屬性,就可能需要定義一個新的實體,新的實體由重複的屬性構成,新實體與原實體之間為一對多關係。在第一正規化(1NF)中表的每一行只包含一個例項的資訊。簡而言之,第一正規化就是無重複的列。

第二正規化(2NF):

  • 是在第一正規化(1NF)的基礎上建立起來的,即滿足第二正規化(2NF)必須先滿足第一正規化(1NF)。第二正規化(2NF)要求資料庫表中的每個例項或行必須可以被惟一地區分。為實現區分通常需要為表加上一個列,以儲存各個例項的惟一標識。這個惟一屬性列被稱為主關鍵字或主鍵、主碼。
  • 第二正規化(2NF)要求實體的屬性完全依賴於主關鍵字。所謂完全依賴是指不能存在僅依賴主關鍵字一部分的屬性,如果存在,那麼這個屬性和主關鍵字的這一部分應該分離出來形成一個新的實體,新實體與原實體之間是一對多的關係。為實現區分通常需要為表加上一個列,以儲存各個例項的惟一標識。簡而言之,第二正規化就是非主屬性非部分依賴於主關鍵字。

第三正規化(3NF):

  • 滿足第三正規化(3NF)必須先滿足第二正規化(2NF)。簡而言之,第三正規化(3NF)要求一個數據庫表中不包含已在其它表中已包含的非主關鍵字資訊。例如,存在一個部門資訊表,其中每個部門有部門編號(dept_id)、部門名稱、部門簡介等資訊。那麼在員工資訊表中列出部門編號後就不能再將部門名稱、部門簡介等與部門有關的資訊再加入員工資訊表中。如果不存在部門資訊表,則根據第三正規化(3NF)也應該構建它,否則就會有大量的資料冗餘。簡而言之,第三正規化就是屬性不依賴於其它非主屬性。

SQL語句優化

  • 應儘量避免在 where 子句中使用!=或<>操作符,否則將引擎放棄使用索引而進行全表掃描;
  • 應儘量避免在 where 子句中對欄位進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描。如:select id from t where num is null可以在num上設定預設值0,確保表中num列沒有null值,然後這樣查詢:select id from t where num=0;
  • 很多時候用 exists 代替 in 是一個好的選擇;
  • 用Where子句替換HAVING 子句 因為HAVING 只會在檢索出所有記錄之後才對結果集進行過濾;
  • 在表中建立索引,優先考慮where、group by使用到的欄位;
  • 儘量避免使用select *,返回無用的欄位會降低查詢效率;
  • 儘量避免使用in和not in,會導致資料庫引擎放棄索引進行全表掃描;
  • 儘量避免使用or,會導致資料庫引擎放棄索引進行全表掃描;
  • 儘量避免在欄位開頭模糊查詢,會導致資料庫引擎放棄索引進行全表掃描。

資料庫結構優化

  • 正規化優化: 比如消除冗餘(節省空間);
  • 反正規化優化:比如適當加冗餘等(減少join);
  • 拆分表(垂直拆分和水平拆分): 分割槽將資料在物理上分隔開,不同分割槽的資料可以制定儲存在處於不同磁碟上的資料檔案裡。

MySQL中myisam與innodb的區別

  • InnoDB支援事物,而MyISAM不支援事物;
  • InnoDB支援行級鎖,而MyISAM支援表級鎖;
  • InnoDB支援MVCC, 而MyISAM不支援;
  • InnoDB支援外來鍵,而MyISAM不支援;
  • InnoDB不支援全文索引,而MyISAM支援;
  • InnoDB不能通過直接拷貝表文件的方法拷貝表到另外一臺機器, myisam 支援;
  • InnoDB表支援多種行格式, myisam 不支援;
  • InnoDB是索引組織表, myisam 是堆表。

Innodb引擎的4大特性

  • 插入緩衝(insert buffer)
  • 二次寫(double write)
  • 自適應雜湊索引(ahi)
  • 預讀(read ahead)

四種事務隔離級別

  • 讀未提交(read uncommitted) :可以讀取其他 session 未提交的髒資料。
  • 讀已提交(read committed) :允許不可重複讀取,但不允許髒讀取。提交後,其他會話可以看到提交的資料。
  • 可重複讀(repeatable read) :禁止不可重複讀取和髒讀取、以及幻讀(innodb 獨有)。
  • 序列(serializable):事務只能一個接著一個地執行,但不能併發執行。事務隔離級別最高。
事務隔離級別髒讀不可重複讀幻讀
讀未提交(read-uncommitted)
不可重複讀(read-committed)
可重複讀(repeatable-read)
序列化(serializable)

注:mysql預設是 可重複讀(repeatable-read),oracle預設是讀已提交(read committed)。

MySQL B+Tree索引和Hash索引的區別

  • Hash索引結構的特殊性,其檢索效率非常高,索引的檢索可以一次定位;
  • B+樹索引需要從根節點到枝節點,最後才能訪問到頁節點這樣多次的IO訪問。

樂觀鎖和悲觀鎖

  • 悲觀鎖(Pessimistic Lock)的特點是先獲取鎖,再進行業務操作,即“悲觀”的認為獲取鎖是非常有可能失敗的,因此要先確保獲取鎖成功再進行業務操作。通常所說的“一鎖二查三更新”即指的是使用悲觀鎖。通常來講在資料庫上的悲觀鎖需要資料庫本身提供支援,即通過常用的select … for update操作來實現悲觀鎖。當資料庫執行select for update時會獲取被select中的資料行的行鎖,因此其他併發執行的select for update如果試圖選中同一行則會發生排斥(需要等待行鎖被釋放),因此達到鎖的效果。select for update獲取的行鎖會在當前事務結束時自動釋放,因此必須在事務中使用。

  • 樂觀鎖(Optimistic Lock),也叫樂觀併發控制,它假設多使用者併發的事務在處理時不會彼此互相影響,各事務能夠在不產生鎖的情況下處理各自影響的那部分資料。在提交資料更新之前,每個事務會先檢查在該事務讀取資料後,有沒有其他事務又修改了該資料。如果其他事務有更新的話,那麼當前正在提交的事務會進行回滾。樂觀鎖的特點先進行業務操作,不到萬不得已不去拿鎖。即“樂觀”的認為拿鎖多半是會成功的,因此在進行完業務操作需要實際更新資料的最後一步再去拿一下鎖就好。

總結:

悲觀鎖和樂觀鎖是資料庫用來保證資料併發安全防止更新丟失的兩種方法,例子在select … for update前加個事務就可以防止更新丟失。悲觀鎖和樂觀鎖大部分場景下差異不大,一些獨特場景下有一些差別,一般我們可以從如下幾個方面來判斷。

  • 響應速度:如果需要非常高的響應速度,建議採用樂觀鎖方案,成功就執行,不成功就失敗,不需要等待其他併發去釋放鎖。
  • 衝突頻率:如果衝突頻率非常高,建議採用悲觀鎖,保證成功率,如果衝突頻率大,樂觀鎖會需要多次重試才能成功,代價比較大。
  • 重試代價:如果重試代價大,建議採用悲觀鎖。

非關係型資料庫和關係型資料庫區別

非關係型資料庫的優勢:

  • 效能:NOSQL是基於鍵值對的,可以想象成表中的主鍵和值的對應關係,而且不需要經過SQL層的解析,所以效能非常高。
  • 可擴充套件性:同樣也是因為基於鍵值對,資料之間沒有耦合性,所以非常容易水平擴充套件。

關係型資料庫的優勢:

  • 複雜查詢:可以用SQL語句方便的在一個表以及多個表之間做非常複雜的資料查詢。
  • 事務支援:使得對於安全效能很高的資料訪問要求得以實現。

EXPLAIN

語法:

EXPLAINSELECT
  • EXPLAIN EXTENDED SELECT 將執行計劃“反編譯”成SELECT語句,執行SHOW WARNINGS 可得到被MySQL優化器優化後的查詢語句 ;
  • EXPLAIN PARTITIONS SELECT 用於分割槽表的EXPLAIN;
  • 在Navicat圖形化介面中,點選“解釋”出現執行計劃的資訊。

執行計劃中的資訊:

1.id:包含一組數字,表示查詢中執行select子句或操作表的順序。id相同,可以認為是一組,從上往下順序執行;在所有組中,id值越大,優先順序越高,越先執行。

2.select_type:主要用於區別普通查詢, 聯合查詢, 子查詢等複雜查詢。

  • SIMPLE:查詢中不包含子查詢或者UNION
  • 查詢中若包含任何複雜的子部分,最外層查詢則被標記為:PRIMARY
  • 在SELECT或WHERE列表中包含了子查詢,該子查詢被標記為:SUBQUERY
  • 在FROM列表中包含的子查詢被標記為:DERIVED(衍生)
  • 若第二個SELECT出現在UNION之後,則被標記為UNION;若UNION包含在FROM子句的子查詢中,外層SELECT將被標記為:DERIVED
  • 從UNION表獲取結果的SELECT被標記為:UNION RESULT

3.type:表示MySQL在表中找到所需行的方式,又稱“訪問型別”(ALL、index、range、ref、eq_ref、const、system、NULL),由左至右,由最差到最好

  • ALL:Full Table Scan, MySQL將遍歷全表以找到匹配的行
  • index:Full Index Scan,index與ALL區別為index型別只遍歷索引樹
  • range:索引範圍掃描,對索引的掃描開始於某一點,返回匹配值域的行,常見於between、<、>等的查詢
  • ref:非唯一性索引掃描,返回匹配某個單獨值的所有行。常見於使用非唯一索引即唯一索引的非唯一字首進行的查詢
  • eq_ref:唯一性索引掃描,對於每個索引鍵,表中只有一條記錄與之匹配。常見於主鍵或唯一索引掃描
  • const、system:當MySQL對查詢某部分進行優化,並轉換為一個常量時,使用這些型別訪問。如將主鍵置於where列表中,MySQL就能將該查詢轉換為一個常量
  • system是const型別的特例,當查詢的表只有一行的情況下, 使用system
  • NULL:MySQL在優化過程中分解語句,執行時甚至不用訪問表或索引

4.possible_keys:指出MySQL能使用哪個索引在表中找到行,查詢涉及到的欄位上若存在索引,則該索引將被列出,但不一定被查詢使用

5.key:顯示MySQL在查詢中實際使用的索引,若沒有使用索引,顯示為NULL
查詢中若使用了覆蓋索引,則該索引僅出現在key列表中

6.key_len:表示索引中使用的位元組數,可通過該列計算查詢中使用的索引的長度
key_len顯示的值為索引欄位的最大可能長度,並非實際使用長度,即key_len是根據表定義計算而得,不是通過表內檢索出的

7.ref:表示上述表的連線匹配條件,即哪些列或常量被用於查詢索引列上的值

8.rows:表示MySQL根據表統計資訊及索引選用情況,估算的找到所需的記錄所需要讀取的行數

9.Extra:包含不適合在其他列中顯示但十分重要的額外資訊

  • Using index:該值表示相應的select操作中使用了覆蓋索引(Covering Index)。
  • MySQL可以利用索引返回select列表中的欄位,而不必根據索引再次讀取資料檔案包含所有滿足查詢需要的資料的索引稱為 覆蓋索引(Covering Index)
  • Using where:表示MySQL伺服器在儲存引擎受到記錄後進行“後過濾”(Post-filter),如果查詢未能使用索引,Using where的作用只是提醒我們MySQL將用where子句來過濾結果集
  • Using temporary:表示MySQL需要使用臨時表來儲存結果集,常見於排序和分組查詢
  • Using filesort:MySQL中無法利用索引完成的排序操作稱為“檔案排序”

注意:如果要使用覆蓋索引,一定要注意select列表中只取出需要的列,不可select *,因為如果將所有欄位一起做索引會導致索引檔案過大,查詢效能下降

執行計劃的侷限:

  • EXPLAIN不會告訴你關於觸發器、儲存過程的資訊或使用者自定義函式對查詢的影響情況
  • EXPLAIN不考慮各種Cache
  • EXPLAIN不能顯示MySQL在執行查詢時所作的優化工作
  • 部分統計資訊是估算的,並非精確值
  • EXPALIN只能解釋SELECT操作,其他操作要重寫為SELECT後檢視執行計劃

Redis

概念

Redis 是一個開源的使用 ANSI C 語言編寫、遵守 BSD 協議、支援網路、可基於記憶體亦可持久化的日誌型、Key-Value 資料庫,並提供多種語言的 API的非關係型資料庫。

傳統資料庫遵循 ACID 規則。而 Nosql(Not Only SQL 的縮寫,是對不同於傳統的關係型資料庫的資料庫管理系統的統稱) 一般為分散式而分散式一般遵循 CAP 定理。

Redis支援的資料型別

  • String:string型別是二進位制安全的。意思是redis的string可以包含任何資料。比如jpg圖片或者序列化的物件 。string型別是Redis最基本的資料型別,一個鍵最大能儲存512MB;
  • Hash:hash 是一個鍵值(key=>value)對集合。Redis hash是一個string型別的field和value的對映表,hash特別適合用於儲存物件;
  • List:列表是簡單的字串列表,按照插入順序排序。你可以新增一個元素到列表的頭部(左邊)或者尾部(右邊);
  • Set:Set是string型別的無序集合。集合是通過雜湊表實現的,所以新增,刪除,查詢的複雜度都是O(1);
  • zset(有序集合): zset 和 set 一樣也是string型別元素的集合,且不允許重複的成員。不同的是每個元素都會關聯一個double型別的分數。redis正是通過分數來為集合中的成員進行從小到大的排序。zset的成員是唯一的,但分數(score)卻可以重複。

Redis的持久化

1.持久化就是把記憶體的資料寫到磁碟中去,防止服務宕機了記憶體資料丟失。Redis 提供了兩種持久化方式:

  • RDB(預設):Redis DataBase縮寫,功能核心函式rdbSave(生成RDB檔案)和rdbLoad(從檔案載入記憶體)兩個函式;
  • AOF :Append-only file縮寫,每當執行伺服器(定時)任務或者函式時flushAppendOnlyFile 函式都會被呼叫, 這個函式執行以下兩個工作aof寫入儲存:1).WRITE:根據條件,將 aof_buf 中的快取寫入到 AOF 檔案;2).SAVE:根據條件,呼叫 fsync 或 fdatasync 函式,將 AOF 檔案儲存到磁碟中。

2.儲存結構:內容是redis通訊協議(RESP )格式的命令文字儲存。

3.RDB和AOF的比較:

  • aof檔案比rdb更新頻率高,優先使用aof還原資料;
  • aof比rdb更安全也更大;
  • rdb效能比aof好;
  • 如果兩個都配了優先載入AOF。

4.RESP: 是redis客戶端和服務端之前使用的一種通訊協議;RESP 的特點:實現簡單、快速解析、可讀性好。

Redis的架構模式

單機版:

  • 特點:簡單
  • 問題:記憶體容量有限 、處理能力有限 、無法高可用。

主從複製:

Redis 的複製(replication)功能允許使用者根據一個 Redis 伺服器來建立任意多個該伺服器的複製品,其中被複制的伺服器為主伺服器(master),而通過複製創建出來的伺服器複製品則為從伺服器(slave)。 只要主從伺服器之間的網路連線正常,主從伺服器兩者會具有相同的資料,主伺服器就會一直將發生在自己身上的資料更新同步 給從伺服器,從而一直保證主從伺服器的資料相同。

  • 特點:master/slave 角色、master/slave 資料相同、降低 master 讀壓力在轉交從庫;
  • 問題:無法保證高可用、沒有解決 master 寫的壓力。

哨兵:

Redis sentinel 是一個分散式系統中監控 redis 主從伺服器,並在主伺服器下線時自動進行故障轉移。

1.特性:

  • 監控(Monitoring): Sentinel 會不斷地檢查你的主伺服器和從伺服器是否運作正常。
  • 提醒(Notification): 當被監控的某個 Redis 伺服器出現問題時, Sentinel 可以通過 API 向管理員或者其他應用程式傳送通知。
  • 自動故障遷移(Automatic failover): 當一個主伺服器不能正常工作時, Sentinel 會開始一次自動故障遷移操作。

2.特點:保證高可用、監控各個節點、自動故障遷移;

3.缺點:主從模式,切換需要時間丟資料、沒有解決 master 寫的壓力。

叢集(proxy 型):

Twemproxy 是一個 Twitter 開源的一個 redis 和 memcache 快速/輕量級代理伺服器; Twemproxy 是一個快速的單執行緒代理程式,支援 Memcached ASCII 協議和 redis 協議。

1.特點:

  • 多種 hash 演算法:MD5、CRC16、CRC32、CRC32a、hsieh、murmur、Jenkins ;
  • 支援失敗節點自動刪除;
  • 後端 Sharding 分片邏輯對業務透明,業務方的讀寫方式和操作單個 Redis 一致。

2.缺點:增加了新的 proxy,需要維護其高可用。

叢集(直連型):

從redis 3.0之後版本支援redis-cluster叢集,Redis-Cluster採用無中心結構,每個節點儲存資料和整個叢集狀態,每個節點都和其他所有節點連線。

1.特點:

  • 無中心架構(不存在哪個節點影響效能瓶頸),少了 proxy 層;
  • 資料按照 slot 儲存分佈在多個節點,節點間資料共享,可動態調整資料分佈;
  • 可擴充套件性,可線性擴充套件到 1000 個節點,節點可動態新增或刪除;
  • 高可用性,部分節點不可用時,叢集仍可用。通過增加 Slave 做備份資料副本;
  • 實現故障自動 failover,節點之間通過 gossip 協議交換狀態資訊,用投票機制完成 Slave到 Master 的角色提升。

2.缺點:

  • 資源隔離性較差,容易出現相互影響的情況;
  • 資料通過非同步複製,不保證資料的強一致性。

快取穿透和快取雪崩

快取穿透:

1.理解一:一般的快取系統,都是按照key去快取查詢,如果不存在對應的value,就應該去後端系統查詢(比如DB)。一些惡意的請求會故意查詢不存在的key,請求量很大,就會對後端系統造成很大的壓力。這就叫做快取穿透。

2.如何避免:

  • 對查詢結果為空的情況也進行快取,快取時間設定短一點,或者該key對應的資料insert了之後清理快取。
  • 對一定不存在的key進行過濾。可以把所有的可能存在的key放到一個大的Bitmap中,查詢時通過該bitmap過濾。

3.理解二:快取穿透是指查詢一個一定不存在的資料。由於快取不命中,並且出於容錯考慮,如果從資料庫查不到資料則不寫入快取,這將導致這個不存在的資料每次請求都要到資料庫去查詢,失去了快取的意義。

請求的資料在快取大量不命中,導致請求走資料庫。

4.解決方法:

  • 由於請求的引數是不合法的(每次都請求不存在的引數),於是我們可以使用布隆過濾器(BloomFilter)或者壓縮filter提前攔截,不合法就不讓這個請求到資料庫層;
  • 當我們從資料庫找不到的時候,我們也將這個空物件設定到快取裡邊去。下次再請求的時候,就可以從快取裡邊獲取了。

快取雪崩:

1.理解一:當快取伺服器重啟或者大量快取集中在某一個時間段失效,這樣在失效的時候,會給後端系統帶來很大壓力。導致系統崩潰。

2.如何避免:

  • 在快取失效後,通過加鎖或者佇列來控制讀資料庫寫快取的執行緒數量。比如對某個key只允許一個執行緒查詢資料和寫快取,其他執行緒等待。
  • 做二級快取,A1為原始快取,A2為拷貝快取,A1失效時,可以訪問A2,A1快取失效時間設定為短期,A2設定為長期
  • 不同的key,設定不同的過期時間,讓快取失效的時間點儘量均勻。

3.理解二:

Redis掛掉了,請求全部走資料庫;

對快取資料設定相同的過期時間,導致某段時間內快取失效,請求全部走資料庫。

4.解決方法:

  • 對於“Redis掛掉了,請求全部走資料庫”這種情況,可以有以下的思路:

事發前:實現Redis的高可用(主從架構+Sentinel 或者Redis Cluster),儘量避免Redis掛掉這種情況發生;
事發中:萬一Redis真的掛了,我們可以設定本地快取(ehcache)+限流(hystrix),儘量避免我們的資料庫被幹掉(起碼能保證我們的服務還是能正常工作的);
事發後:redis持久化,重啟後自動從磁碟上載入資料,快速恢復快取資料。

  • 對於“對快取資料設定相同的過期時間,導致某段時間內快取失效,請求全部走資料庫。”這種情況,非常好解決:

在快取的時候給過期時間加上一個隨機值,這樣就會大幅度的減少快取在同一時間過期。

為什麼redis需要把所有資料放到記憶體中

Redis為了達到最快的讀寫速度將資料都讀到記憶體中,並通過非同步的方式將資料寫入磁碟。所以redis具有快速和資料持久化的特徵。如果不將資料放在記憶體中,磁碟I/O速度為嚴重影響redis的效能。在記憶體越來越便宜的今天,redis將會越來越受歡迎。如果設定了最大使用的記憶體,則資料已有記錄數達到記憶體限值後不能繼續插入新值。

Redis的單程序單執行緒

Redis利用佇列技術將併發訪問變為序列訪問,消除了傳統資料庫序列控制的開銷。

Redis的回收策略

  • volatile-lru:從已設定過期時間的資料集(server.db[i].expires)中挑選最近最少使用的資料淘汰;
  • volatile-ttl:從已設定過期時間的資料集(server.db[i].expires)中挑選將要過期的資料淘汰;
  • volatile-random:從已設定過期時間的資料集(server.db[i].expires)中任意選擇資料淘汰;
  • allkeys-lru:從資料集(server.db[i].dict)中挑選最近最少使用的資料淘汰;
  • allkeys-random:從資料集(server.db[i].dict)中任意選擇資料淘汰;
  • no-enviction(驅逐):禁止驅逐資料。

使用Redis的好處

  • 速度快:因為資料存在記憶體中,類似於HashMap,HashMap的優勢就是查詢和操作的時間複雜度都是O(1);
  • 支援豐富資料型別:支援string,list,set,sorted set,hash;
  • 支援事務:操作都是原子性,所謂的原子性就是對資料的更改要麼全部執行,要麼全部不執行;
  • 豐富的特性:可用於快取,訊息,按key設定過期時間,過期後將會自動刪除。

Redis 最適合的場景

會話快取(Session Cache)

最常用的一種使用Redis的情景是會話快取(session cache)。用Redis快取會話比其他儲存(如Memcached)的優勢在於:Redis提供持久化。當維護一個不是嚴格要求一致性的快取時,如果使用者的購物車資訊全部丟失,大部分人都會不高興的,現在,他們還會這樣嗎?幸運的是,隨著 Redis 這些年的改進,很容易找到怎麼恰當的使用Redis來快取會話的文件。甚至廣為人知的商業平臺Magento也提供Redis的外掛。

全頁快取(FPC)

除基本的會話token之外,Redis還提供很簡便的FPC平臺。回到一致性問題,即使重啟了Redis例項,因為有磁碟的持久化,使用者也不會看到頁面載入速度的下降,這是一個極大改進,類似PHP本地FPC。再次以Magento為例,Magento提供一個外掛來使用Redis作為全頁快取後端。此外,對WordPress的使用者來說,Pantheon有一個非常好的外掛 wp-redis,這個外掛能幫助你以最快速度載入你曾瀏覽過的頁面。

佇列

Reids在記憶體儲存引擎領域的一大優點是提供 list 和 set 操作,這使得Redis能作為一個很好的訊息佇列平臺來使用。Redis作為佇列使用的操作,就類似於本地程式語言(如Python)對 list 的 push/pop 操作。

如果你快速的在Google中搜索“Redis queues”,你馬上就能找到大量的開源專案,這些專案的目的就是利用Redis建立非常好的後端工具,以滿足各種佇列需求。例如,Celery有一個後臺就是使用Redis作為broker,你可以從這裡去檢視。

排行榜/計數器

Redis在記憶體中對數字進行遞增或遞減的操作實現的非常好。集合(Set)和有序集合(Sorted Set)也使得我們在執行這些操作的時候變的非常簡單,Redis只是正好提供了這兩種資料結構。所以,我們要從排序集合中獲取到排名最靠前的10個使用者–我們稱之為“user_scores”,我們只需要像下面一樣執行即可:

當然,這是假定你是根據你使用者的分數做遞增的排序。如果你想返回使用者及使用者的分數,你需要這樣執行:

ZRANGE user_scores 0 10 WITHSCORES

Agora Games就是一個很好的例子,用Ruby實現的,它的排行榜就是使用Redis來儲存資料的,你可以在這裡看到。

釋出/訂閱

Redis的釋出/訂閱功能。釋出/訂閱的使用場景確實非常多。我已看見人們在社交網路連線中使用,還可作為基於釋出/訂閱的指令碼觸發器,甚至用Redis的釋出/訂閱功能來建立聊天系統。

Redis支援的Java客戶端

  • Redisson(官方推薦使用):是一個高階的分散式協調Redis客服端,能幫助使用者在分散式環境中輕鬆實現一些Java的物件;
  • Jedis:Jedis是Redis的Java實現的客戶端,其API提供了比較全面的Redis命令的支援;
  • lettuce

Redisson和Jedis的區別:Redisson實現了分散式和可擴充套件的Java資料結構,和Jedis相比,功能較為簡單,不支援字串操作,不支援排序、事務、管道、分割槽等Redis特性。Redisson的宗旨是促進使用者對Redis的關注分離,從而讓使用者能夠將精力更集中地放在處理業務邏輯上。

Redis雜湊槽

Redis叢集沒有使用一致性hash,而是引入了雜湊槽的概念,Redis叢集有16384個雜湊槽,每個key通過CRC16校驗後對16384取模來決定放置哪個槽,叢集的每個節點負責一部分hash槽

Redis管道的作用

一次請求/響應伺服器能實現處理新的請求即使舊的請求還未被響應。這樣就可以將多個命令傳送到伺服器,而不用等待回覆,最後在一個步驟中讀取該答覆。

Redis事務

事務是一個單獨的隔離操作:事務中的所有命令都會序列化、按順序地執行。事務在執行的過程中,不會被其他客戶端傳送來的命令請求所打斷。事務是一個原子操作:事務中的命令要麼全部被執行,要麼全部都不執行。

Redis事務相關的命令:MULTI、EXEC、DISCARD、WATCH

Redis的分散式事務

對於讀操作

  • 如果我們的資料在快取裡邊有,那麼就直接取快取的;
  • 如果快取裡沒有我們想要的資料,我們會先去查詢資料庫,然後將資料庫查出來的資料寫到快取中;
  • 最後將資料返回給請求。

對於更新操作

一般來說,執行更新操作時,我們會有兩種選擇:

  • 先操作資料庫,再操作快取
  • 先操作快取,再操作資料庫

如果原子性被破壞了,可能會有以下的情況:

  • 操作資料庫成功了,操作快取失敗了。
  • 操作快取成功了,操作資料庫失敗了。

釋出/訂閱

  • 更新快取
  • 刪除快取

一般我們都是採取刪除快取快取策略的,原因如下:

  • 高併發環境下,無論是先操作資料庫還是後操作資料庫而言,如果加上更新快取,那就更加容易導致資料庫與快取資料不一致問題。(刪除快取直接和簡單很多)
  • 如果每次更新了資料庫,都要更新快取【這裡指的是頻繁更新的場景,這會耗費一定的效能】,倒不如直接刪除掉。等再次讀取時,快取裡沒有,那我到資料庫找,在資料庫找到再寫到快取裡邊(體現懶載入)

基於這兩點,對於快取在更新時而言,都是建議執行刪除操作!

釋出/訂閱

  • 先操作資料庫,成功;
  • 再刪除快取,也成功;

如果原子性被破壞了:

  • 第一步成功(操作資料庫),第二步失敗(刪除快取),會導致資料庫裡是新資料,而快取裡是舊資料。
  • 如果第一步(操作資料庫)就失敗了,我們可以直接返回錯誤(Exception),不會出現資料不一致。

如果在高併發的場景下,出現數據庫與快取資料不一致的概率特別低,也不是沒有:

  • 快取剛好失效
  • 執行緒A查詢資料庫,得一箇舊值
  • 執行緒B將新值寫入資料庫
  • 執行緒B刪除快取
  • 執行緒A將查到的舊值寫入快取

要達成上述情況,還是說一句概率特別低:

因為這個條件需要發生在讀快取時快取失效,而且併發著有一個寫操作。而實際上資料庫的寫操作會比讀操作慢得多,而且還要鎖表,而讀操作必需在寫操作前進入資料庫操作,而又要晚於寫操作更新快取,所有的這些條件都具備的概率基本並不大。

刪除快取失敗的解決思路:

  • 將需要刪除的key傳送到訊息佇列中
  • 自己消費訊息,獲得需要刪除的key
  • 不斷重試刪除操作,直到成功

釋出/訂閱

  • 先刪除快取,成功;
  • 再更新資料庫,也成功;

如果原子性被破壞了:

  • 第一步成功(刪除快取),第二步失敗(更新資料庫),資料庫和快取的資料還是一致的。
  • 如果第一步(刪除快取)就失敗了,我們可以直接返回錯誤(Exception),資料庫和快取的資料還是一致的。

看起來是很美好,但是我們在併發場景下分析一下,就知道還是有問題的了:

  • 執行緒A刪除了快取
  • 執行緒B查詢,發現快取已不存在
  • 執行緒B去資料庫查詢得到舊值
  • 執行緒B將舊值寫入快取
  • 執行緒A將新值寫入資料庫

所以也會導致資料庫和快取不一致的問題,併發下解決資料庫與快取不一致的思路:將刪除快取、修改資料庫、讀取快取等的操作積壓到佇列裡邊,實現序列化。

釋出/訂閱

我們可以發現,兩種策略各自有優缺點:

  • 先刪除快取,再更新資料庫
  • 在高併發下表現不如意,在原子性被破壞時表現優異
  • 先更新資料庫,再刪除快取(Cache Aside Pattern設計模式)
  • 在高併發下表現優異,在原子性被破壞時表現不如意

Redis與Memcached的區別與比較

    • Redis不僅僅支援簡單的k/v型別的資料,同時還提供list,set,zset,hash等資料結構的儲存。memcache支援簡單的資料型別,String;
    • Redis支援資料的備份,即master-slave模式的資料備份;
    • Redis支援資料的持久化,可以將記憶體中的資料保持在磁碟中,重啟的時候可以再次載入進行使用,而Memecache把資料全部存在記憶體之中;
    • Redis的速度比memcached快很多;
    • Memcached是多執行緒,非阻塞IO複用的網路模型;Redis使用單執行緒的IO複用模型。