資料庫面試簡答、30道高頻面試題

賜我白日夢發表於2021-01-06


一、MySQL問答


1、資料庫sql語句查詢,跨表查詢有哪幾種方式

內連線(inner可以不寫)

select e.name e.age p.product_name p.saled
from employee e,product p 
where e.id = p.id

select e.name e.age p.product_name p.saled
from employee inner 
join e,product p on e.id = p.id

這就是內連線,它要求資料必須On條件必須百分百匹配才會符合條件並返回。當不滿足時,他會返回空。

外連線是用左\右側的資料去關聯另一側的資料,即使關聯不上任何資料也得把左\右側的資料返回回來。

外連線分(左外連線)和(右外連線)

左外連線( left join)

select g2.Name,Price,ProductionDate,Amount,g1.name
FROM Goods G1
left join GoodsType G2 on G1.Typeld=G2.IO

右外連線(right join--空值的會顯示出來)

select g2.Name,Price,ProductionDate,Amount,g1.name
FROM Goods G1 right join GoodsType G2 on G1.Typeld=G2.IO

全外連線(full outer(可以不寫) join--空值的會顯示出來)

select g1.name,g2.Name,price,productiondate,g2.Amount
FROM GoodsType g1 full outer join Goods g2 on g1.IO=g2.Typeld

交叉連線(笛卡爾積)查詢所有的值

select g1.name,g2.Name,price,productiondate,g2.Amount
FROM GoodsType g1 cross join Goods g2 where g1.IO=g2.Typeld

2、資料庫的索引用到的是什麼資料結構?

答:B+樹

問:那麼B+樹的特點是什麼?為什麼要用這個資料結構?

B+樹是B樹的變種,他們可以是 23樹,234樹,2345樹等等,當單個節點允許伸出1200節點時,三層就可以有17億,因此它體型扁平。。。有利益磁碟IO

B+樹非葉子結點不儲存資料,B樹儲存資料,所以相同大小資料塊,能存更多B+索引

B+樹葉子結點上有雙向連結串列串聯,有利於進行範圍搜尋

B+樹為什麼有利於磁碟IO?

首先了解一下計算機的空間區域性性原理:當一個資料被用到時,其附近的資料也通常會馬上被使用。即使只需要一個位元組,磁碟也會從這個位置開始,順序向後讀取一定長度的資料放入記憶體。

使用紅黑樹(平衡二叉樹)結構的話,每次磁碟預讀中的很多資料是用不上的資料。因此,它沒能利用好磁碟預讀的提供的資料。然後又由於深度大(較B樹而言),所以進行的磁碟IO操作更多。

B樹的每個節點可以儲存多個關鍵字,它將節點大小設定為磁碟頁的大小,充分利用了磁碟預讀的功能。每次讀取磁碟頁時就會讀取一整個節點。也正因每個節點儲存著非常多個關鍵字,樹的深度就會非常的小。進而要執行的磁碟讀取操作次數就會非常少,更多的是在記憶體中對讀取進來的資料進行查詢。

B樹的查詢,主要發生在記憶體中,而平衡二叉樹的查詢,則是發生在磁碟讀取中。因此,雖然B樹查詢查詢的次數不比平衡二叉樹的次數少,但是相比起磁碟IO速度,記憶體中比較的耗時就可以忽略不計了。因此,B樹更適合作為索引。

比B樹更適合作為索引的結構是B+樹。MySQL中也是使用B+樹作為索引。它是B樹的變種,因此是基於B樹來改進的。為什麼B+樹會比B樹更加優秀呢?

B樹:有序陣列+平衡多叉樹。

B+樹:有序陣列連結串列+平衡多叉樹。

B+樹的關鍵字全部存放在葉子節點中,這樣非葉子結點就能在相同的空間儲存更多的資訊,非葉子節點用來做索引,而葉子節點中有一個指標指向一下個葉子節點。做這個優化的目的是為了提高區間訪問的效能。而正是這個特性決定了B+樹更適合用來儲存外部資料。


3、mylsam、innodb的區別

1.InnoDB和MyISAM都是B+數的結構。

2.InnoDB採用MVCC來支援高併發,並且實現了四個標準的隔離級別。其預設級別是REPETABLE READ (可重複讀),並且通過間隙鎖策略防止幻讀的出現。

3.InnoDB表是基於聚簇索引建立的。

4.InnoDB支援事務。

5.InnoDB具有自動崩潰恢復功能。

6.InnoDB支援外來鍵。

MyISAM

1.MyISAM 不支援事務和行級鎖。

2.崩潰後無法安全恢復。

3.對於只讀的資料,或者表比較小,可以忍受修復操作的可以使用。

4.MyISAM會將表儲存在兩個檔案中,資料檔案和索引檔案,分別以.MYD和.MYI為副檔名。

5.MyISAM 支援全文索引。

MyISAM Innodb
儲存結構 每張表被存放在三個檔案:frm-表格定義、MYD(MYData)-資料檔案、MYI(MYIndex)-索引檔案 所有的表都儲存在同一個資料檔案中(也可能是多個檔案,或者是獨立的表空間檔案),InnoDB表的大小隻受限於作業系統檔案的大小,一般為2GB
儲存空間 MyISAM可被壓縮,儲存空間較小 InnoDB的表需要更多的記憶體和儲存,它會在主記憶體中建立其專用的緩衝池用於高速緩衝資料和索引
可移植性、備份及恢復 由於MyISAM的資料是以檔案的形式儲存,所以在跨平臺的資料轉移中會很方便。在備份和恢復時可單獨針對某個表進行操作 免費的方案可以是拷貝資料檔案、備份 binlog,或者用 mysqldump,在資料量達到幾十G的時候就相對痛苦了
檔案格式 資料和索引是分別儲存的,資料.MYD,索引.MYI 資料和索引是集中儲存的,.ibd
記錄儲存順序 按記錄插入順序儲存 按主鍵大小有序插入
外來鍵 不支援 支援
事務 不支援 支援
鎖支援(鎖是避免資源爭用的一個機制,MySQL鎖對使用者幾乎是透明的) 表級鎖定 行級鎖定、表級鎖定,鎖定力度小併發能力高
SELECT MyISAM更優
INSERT、UPDATE、DELETE InnoDB更優
select count(*) myisam更快,因為myisam內部維護了一個計數器,可以直接調取。
索引的實現方式 B+樹索引,myisam 是堆表 B+樹索引,Innodb 是索引組織表
雜湊索引 不支援 支援
全文索引 支援 不支援

4、MySQL的Gtid複製原理是什麼?

mysql主從複製原理就是主庫建立一個專門用於給從庫拉取binlog的賬號,並且給這個賬號授權,讓他可以拉取哪個DB的那個表的binlog,具體的授權SQL是:

grant repliacation slave on xx.xx to username@ip identify by 'password'

這樣從庫就能登陸主庫拉取binlog,那拉取binlog就得知道從哪個binlog的哪個位點拉取,現有的有兩個方案:fileName + position 還有就是通過gtid自動找點。

什麼是GTID?原理?

https://www.cnblogs.com/ZhuChangwu/p/13040214.html


5、同步、半同步、非同步複製原理是什麼?

同步、半同步、非同步複製說的是 從庫在主庫上拉取binlog日誌的模式。

同步:

主庫寫redolog 事物處於prepare狀態、主庫寫binlog,然後從庫拉取binlog去回放,從庫回放成功後返回給主庫ack確認,所有的從庫都完成回放後主庫提交事物。這樣是可以保證主從資料一致的但是缺點就是速度太慢了。

半同步:

主庫寫redolog 事物處於prepare狀態、主庫寫binlog,然後從庫拉取binlog後返回給主庫ack,在眾多從庫中只要收到一個ack主庫就提交事物

非同步複製:

主庫根本不管從庫有沒有拉取回放binlog,直接寫redo、binlog、然後提交事物

首先不允許出現主從資料不一致的情況:如果主從不一致對業務來說是有損的,一旦發生主從資料不一致的情況,從庫就會出現斷開連線的可能。


6、說說你瞭解的MySQL慢查詢?

MySQL有監控項:slow query , MySQL會將所有執行時間超過閾值的SQL記錄到慢查日誌中

我們的監控系統可以監控: 當檢測到有慢查時觸發報警

通常出現慢查到情況如下:

1、表中的資料量很大,而且SQL的執行沒有走索引

2、資料量太大了,即使走了索引依然超過了閾值

3、大量的慢查佔據MySQL連線,導致正常的SQL得不到連線執行從而變成慢查SQL

4、優化器選錯了索引

檢視慢查時間閾值

mysql> show global variables like '%long_query_time%';

+-----------------+-----------+

| Variable_name  | Value   |

+-----------------+-----------+

| long_query_time | 10.000000 |

+-----------------+-----------+

1 row inset (0.00 sec)

檢視執行時間最長的10條SQL

mysqldumpslow -s a1 -n 10 mysql.slow_log

推薦閱讀:https://mp.weixin.qq.com/s/tXTLMCiVpEnnmhUclYR19Q


7、說說MySQL的執行計劃

什麼是執行計劃

每次提交一個SQL到MySQL,MySQL核心的查詢優化器會針對這個SQL的語意生成一個執行計劃,這個執行計劃就代表了他會查哪些表?用哪些索引,如何做排序和分組

執行不同的sql有哪幾種情況

單表查詢舉例:

示例1:

select * fromtablewhere id = x ;
select * fromtablewhere name = x ;

id是主鍵、name是唯一索引像這種可以直接根據聚簇索引或者二級索引+回表就能查詢到我們想要的資料的方式在執行計劃中稱為 const

要求二級索引必須是唯一索引,才屬於const

示例2:

select * fromtablewhere name = x ;

name是普通索引查詢的過程是:從name這個B+樹中查詢出一條記錄後,得到記錄的主鍵id,然後去聚簇索引中回表,這種查詢方式速度也很快,在執行計劃中叫:ref

示例3:

select * fromtablewhere name = x and age = y and xx=yy ;

name、age、xx為普通索引這種sql要求where條件之後的SQL全得是等值比較,在執行計劃中才算做是ref

示例4:

select * fromtablewhere name = x and name is NULL ;

name為普通索引這種sql就是在二級索引中同時搜尋name = x和name = null的值,然後去主庫中回表。這種在執行計劃中被稱為ref_or_null

示例5:

select * fromtablewhere age >=x and age <=y

age是普通索引像這樣使用普通索引做範圍查詢,在執行計劃中稱為 range

示例6:index方式

index方式並不是說執行計劃使用了索引,從聚簇索引中一路二分往下走。

假設有聯合索引:key(x1,x2,x3)

查詢語句如下:

select x1,x2,x3 fromtablewhere x2=xxx;

想使用聯合索引得遵循左字首原則,但是上面直接使用x2,很顯然不符合左字首原則,所以就用不上聯合索引,但是他查詢的x1、x2、x3其實對應聯合索引中的x1、x2、x3所以他會去掃描 聯合索引:key(x1,x2,x3)形成的B+樹,而不是全表掃描,在執行計劃中這就做 index

所以說,index其實是去遍歷二級索引,故他的效率肯定比ref,const、ref_or_null慢,但是比全表掃描快一些

示例7:all

比如你去查詢資料但是不加where條件,就會進行全表掃描

示例8:

select * fromtablewhere x1 = xxx or x2 >= yy ;

然後你的聯合索引是 key1(x1,x3) key2(x2,x4)這時查詢優化器只能在key1和key2中二選一使用,具體選哪一個就看使用哪個索引掃描行數少

比如使用x1掃描行數少,就先拿著x1去過濾一部分資料出來(ref的方式)然後去聚簇索引中回表查詢所有的資料在記憶體中根據第二個條件x2 > yy 再過濾一次

示例9:

select * fromtablewhere x1 = xxx and c1=xxx and c2 >= yy and c3 is null;

只有c1有索引查詢優化器會根據x1,通過ref的方式查詢到一批資料,然後去聚簇索引中回表,將所有符合條件的資料載入進記憶體,然後在記憶體中根據剩下的條件繼續過濾。

示例10:

select * fromtablewhere x1 = xxx and x2=xxx;

x1和x2都有普通索引情況1: 查詢優化器使用x1索引在二級索引中查詢中一批資料,然後將這些資料放到聚簇索引中回表,將資料所有欄位查詢出來,然後在記憶體中根據x2=xxx再過濾。

情況2:查詢優化器使用x1索引在二級索引中查詢中一批資料A,再使用x2索引在二級索引中查詢中一批資料B,兩者做交集,再去聚簇索引中回表,這樣的效率會更高。

多表:

示例1:

select * from t1,t2 where t1.x1 = xxx and t1.x2 = t2.x2and t2.x3 = yyy;

第一步:查詢優化器會根據t1.x1 = xxx這個條件查詢出一部分資料,具體通過ref、index、conf、all根據你索引的情況而定。

假設第一步拿出來了兩條記錄,然後拿著這兩條記錄的x2值和x3值去t2表中去匹配有沒有一樣的,有的話就關聯起來返回,其中t1叫做驅動表,t2叫做被驅動表。

示例2:

巢狀迴圈查詢:簡單來說就是從驅動表中查詢一批資料,然後遍歷這批資料挨個去被驅動表中查詢。

這時如果被驅動表中的使用的該欄位沒有加索引,每次查詢都是all,就會導致連表查詢速度很慢,因此最好兩者都建立索引。

explain時你會關注哪幾個欄位?

答:6個,如下

id:每一個selct語句都有有一個id,複雜的SQL有多個select,就會對應有多個id

select_type: 當前sql的查詢型別

type:ref、index、all、const

possible_keys 可以使用的索引都會放在這裡

rows:掃描的行數

table:查詢的哪張表


8、說說MySQL支援的資料型別

INT(6),6即是其寬度指示器,該寬度指示器並不會影響int列儲存欄位的大小,也就是說,超過6位它不會自動擷取,依然會儲存,只有超過它本身的儲存範圍才會擷取;此處寬度指示器的作用在於該欄位是否有zerofill,如果有就未滿足6位的部分就會用0來填充),

CHAR 型別用於定長字串,並且必須在圓括號內用一個大小修飾符來定義。這個大小修飾符的範圍從 0-255。比指定長度大的值將被截短,而比指定長度小的值將會用空格作填補。

CHAR 型別的一個變體是 VARCHAR 型別。它是一種可變長度的字串型別,並且也必須帶有一個範圍指示器。

CHAR 和 VARCHGAR 不同之處在於 MYSQL 資料庫處理這個指示器的方式:CHAR 把這個大小視為值的大小,不長度不足的情況下就用空格補足。而 VARCHAR 型別把它視為最大值並且只使用儲存字串實際需要的長度(增加一個額外位元組來儲存字串本身的長度)來儲存值。所以短於指示器長度的 VARCHAR 型別不會被空格填補,但長於指示器的值仍然會被截短。

https://www.cnblogs.com/liangxiaofeng/p/5806874.html


9. 瞭解資料庫如何備份嗎

參考: https://www.cnblogs.com/yourblog/p/10381962.html

備份整個資料庫

$> mysqldump -u root -h host -p dbname > backdb.sql

備份資料庫中的某個表

$> mysqldump -u root -h host -p dbname tbname1, tbname2 > backdb.sql

備份多個資料庫

$> mysqldump -u root -h host -p --databases dbname1, dbname2 > backdb.sql

備份系統中所有資料庫

$> mysqldump -u root -h host -p --all-databases > backdb.sql

10. Oracle和Mysql的區別

巨集觀上:

  1. Mysql是小型資料庫, 開源, 免費, Oracle收費

  2. Oracle支援大併發, 大訪問量

  3. MySql中安裝後佔用的記憶體小, Oracle不僅佔用記憶體大, 而且越用越大

微觀上:

  1. Mysql對事務預設不支援, 但是它的儲存引擎 InnoDB支援事務, Oracle對事務完全支援

  2. 併發性: MySQL早期的資料引擎MyISAM是支援表級鎖, 後來的InnoDB才支援行級鎖, Oracle支援行級鎖

  3. Oracle會將提交的sql寫入連線日誌中, 然後寫入磁碟, 保證不會丟失資料, MySql在執行更新的操作時可能會丟失資料

  4. 隔離級別不同:

    a. Oracle預設使用 read commited 讀已經提交

    b. MySQL預設使用的是 repeatable read 可重複讀

  5. 提交方式

    a. Oracle 預設不會自動提交事務

    b. MySQL預設自動提交事務

  6. 邏輯備份

    a. Mysql 的資料備份會鎖定資料, 影響正常的DML

    b. Oracle在資料備份時, 不會鎖定任何資料

  7. 資料插入

    a. Mysql會更加靈活一點, 比如limit分頁, insert插入多行資料

    b. Oracle的分頁使用偽列+子查詢實現 , 插入資料也只能一行行插入

  8. 許可權控制:

    a. Oracle的許可權控制是中規中矩的, 和系統使用者無關

    b. MySQL的許可權控制和主機相關, 感覺沒啥意義

  9. 效能診斷

    a. Oracle 有大量的效能診斷工具, 可以實現自動分析

    b. Mysql效能診斷方法很少, 主要就是通過通過慢查詢日誌去排查

  10. 分割槽表和分割槽索引

    a. Oracle的分割槽表和分割槽索引相對來說比較成熟

    b. Mysql 分割槽表和分割槽索引就不成熟

  11. 資料複製

    a. 在搭建的主從複製的模式中, 主庫出現了問題, 可能會導致從庫有一定資料的丟失, 需要手動的切換的到主庫

    b. Oracle 則更強大, 既有傳統的推/拉式的資料複製, 同時也有 dataguard雙機或者多機的容災機制, 而且主庫出現問題, 自動切換到備庫, 但是配置相對複雜


11. 事務的四種特性

ACID:

  1. Atomic 原子性: 事務不能被分割, 要麼都做, 要麼都不做。

  2. Consistency 一致性: 可以用轉賬的例子解釋一致性。

  3. Isolation 隔離性 : 不同的事務, 彼此隔離, 互不干擾。

  4. Durability 永續性: 也叫做用就行, 事務一旦被提交, 對資料庫做出的修改將被持久化 。


12. 四種隔離級別以及什麼是髒讀,幻讀,不可重複讀

  1. read uncommitted 讀未提交: 在事務A中讀取到了事務B中未提交的資料, 也叫做髒讀。

  2. read commited 讀已提交: Oracle預設使用的隔離級別, 讀已提交, 說白了, 事務A先開啟, 然後事務B再開啟, 然後事務Bcommit一個事務操作, 修改資料 , 那麼這個修改是能被事務A讀取到的, 這就叫做讀已提交, 也是所謂的不可重複讀,(因為重複讀之後, 資料可能會發生變化)。

  3. repeatable read : 可重複讀, 這也是Mysql預設的事務隔離級別, 事務A開啟後, 無論讀取多少次, 得到的結果都和第一次得到的結果是一樣的, 但是如果事務B在事務A第一次讀取的範圍內插入了一條資料的話, 會發生幻讀, 兩次讀取結果又不一致了, Mysql的InnoDB引擎通過多版本併發控制MVCC解決了這個問題。

  4. serializable : 可序列化, 最高的事務隔離級別, 到是也是效率最低的事務隔離級別。

13. MySQL中 主鍵索引、普通索引、唯一索引的區別

主鍵索引 primary key:

  • 一個表只能有一個主鍵索引。

  • 主鍵索引不能為空。

  • 主鍵索引可以做外來鍵。

唯一索引unique key:

  • 一張表可以存在多個唯一索引。

  • 唯一索引可以是一列或者多列。

  • 唯一索引不可重複的。

  • 因為這個原因, 限制唯一索引做多有一個null。

普通索引 normal key :

  • 普通一般是為了加快資料的訪問速度而建立的。

  • 針對那些經常被查詢, 或者經常被排序的欄位建立。

  • 被索引的資料允許出現重複的值。


14. 資料庫三大正規化

第一大正規化:

關係模式R中的所有屬性都不能再分解, 稱關係模式R 滿足第一正規化, 比如 address 欄位就可以繼續拆分成 省市區, 我們就可以認為address不滿足第一正規化。

第二大正規化:

在滿足第一正規化的基礎上更進一步, 它要求所有的非主屬性都必須完全依賴於第一正規化中確定下來的主屬性, 換句話說, 比如聯合主鍵就不符合第二正規化, 因為很有可能這個表中的一部分非主屬性和聯合主鍵中的一部分列是有依賴關係的, 而和另外一部分並沒有依賴關係。

第三大正規化:

在第一正規化R的基礎上, 更進一步, 要求所有的欄位都可主鍵直接相關而不能間接相關, 比如使用者表裡面不要出現訂單表中的訂單資訊。

15. sql語句各種條件的執行順序,如select, where, order by, group by

from where group by having order by limit select 

16. 求表的size,或做資料統計可用什麼儲存引擎

查詢資料表所佔的容量

select sum(DATA_LENGTH) + sum(INDEX_LENGTH) from information_schema.tables where table_schema = '資料庫名

查詢所有資料的大小, 用兆的方式輸出結果

select concat(round(sum(DATA_LENGTH/1024/1024),2),'MB') asdata
from information_schema.tables
where table_schema='blog'andtable_name='catalog'

17. 讀多寫少可用什麼引擎

MyISAM 它在設計之時就考慮到 資料庫被查詢的次數要遠大於更新的次數。因此,ISAM執行讀取操作的速度很快,而且不佔用大量的記憶體和儲存資源。

所以, 如果系統中的寫操作真的很少,並且不使用mysql的事務等高階操作的話, 建議使用MYISAM。


18. 假如要統計多個表應該用什麼引擎

考慮報表引擎


19.MySQL Explain各欄位意思

欄位名 含義
id 選擇識別符號
select_type 表示查詢的型別
table 輸出結果集的表
partitions 匹配的分割槽
type 表示表的連線型別
possible_keys 表示查詢時,可能使用的索引
key 表示實際使用的索引
key_len 索引欄位的長度
ref 列與索引的比較
rows 掃描出的行數(估算的行數)
filtered 按表條件過濾的行百分比
Extra 執行情況的描述和說明

20.索引設計的原則?

  1. 適合索引的列是出現在where子句中的列,或者連線子句中指定的列。

  2. 基數較小的類,索引效果較差,沒有必要在此列建立索引。

  3. 使用短索引,如果對長字串列進行索引,應該指定一個字首長度,這樣能夠節省大量索引空間。

  4. 不要過度索引。索引需要額外的磁碟空間,並降低寫操作的效能。在修改表內容的時候,索引會進行更新甚至重構,索引列越多,這個時間就會越長。所以只保持需要的索引有利於查詢即可。


21. MySQL有關許可權的表有哪幾個?

表名 含義
user許可權表 記錄允許連線到伺服器的使用者帳號資訊,裡面的許可權是全域性級的。
db許可權表 記錄各個帳號在各個資料庫上的操作許可權。
table_priv許可權表 記錄資料表級的操作許可權。
columns_priv許可權表 記錄資料列級的操作許可權。
host許可權表 配合db許可權表對給定主機上資料庫級操作許可權作更細緻的控制。這個許可權表不受GRANT和REVOKE語句的影響。

二、白日夢的MySQL專題

1、談談MySQL中基數是什麼?

2、聊聊什麼是慢查?如何監控?如何排查?

3、對Not Null欄位插入Null值有啥現象?

4、能談談year、date、datetime、time、timestamp的區別嗎?

5、你有沒有搞混查詢快取和Buffer Pool?談談看!

6、你知道資料庫緩衝池中的LRU-List嗎?

7、瞭解InnoDB的FreeList嗎?談談看!

8、瞭解Flush-List嗎?順便說一下髒頁的落盤機制!

9、用 11 張圖講清楚,當你CRUD時BufferPool中發生了什麼!以及BufferPool的優化!

10、瞭解 MySQL的表空間 和 資料表嗎?談談看!

11、瞭解 MySQL的資料行嗎?行溢位機制呢?談談看!

12、瞭解MySQL資料頁嗎?說說什麼是頁分裂吧!

13、用一分鐘瞭解fsync這個系統呼叫

14、簡述undo log、truncate、以及undo log如何幫你回滾事務?

15、我勸!這位年輕人不講MVCC,耗子尾汁!

16、傳說中的MySQL的redo log是什麼?談談看!

17、LSN、Checkpoint?談談MYSQL的崩潰恢復是怎麼回事!

18、MySQL的 bin log有啥用?在哪裡?誰寫的?怎麼配置?

19、bin log有哪些格式?有啥區別?優缺點?線上用哪種格式?

20、MySQL的修仙之路,圖文談談如何學MySQL、如何進階!

文章公眾號首發,連載中,歡迎關注白日夢,一起衝鴨!

文章公眾號首發,連載中,歡迎關注白日夢,一起衝鴨!

文章公眾號首發,連載中,歡迎關注白日夢,一起衝鴨!

三、Redis問答


1. redis能存哪些型別

string == Map<String,String>
map    == Map<String,Map<String>>
list   == Map<String , List<String>>
set    == Map<String,Set<String>>
zset   == Map<String,ZSet<String>>

2、redis為什麼這麼快? 高併發如何處理的?

高併發的原因:

1.redis是基於記憶體的,記憶體的讀寫速度非常快;

2.redis是單執行緒的,省去了很多上下文切換執行緒的時間;

3.redis使用多路複用技術,可以處理併發的連線。非阻塞IO 內部實現採用epoll,採用了epoll+自己實現的簡單的事件框架。epoll中的讀、寫、關閉、連線都轉化成了事件,然後利用epoll的多路複用特性,絕不在io上浪費一點時間。

為什麼Redis是單執行緒的:

官方答案: 因為Redis是基於記憶體的操作,CPU不是Redis的瓶頸,Redis的瓶頸最有可能是機器記憶體的大小或者網路頻寬。既然單執行緒容易實現,而且CPU不會成為瓶頸,那就順理成章地採用單執行緒的方案了。

不需要各種鎖的效能消耗

Redis的資料結構並不全是簡單的Key-Value,還有list,hash等複雜的結構,這些結構有可能會進行很細粒度的操作,比如在很長的列表後面新增一個元素,在hash當中新增或者刪除

一個物件。這些操作可能就需要加非常多的鎖,導致的結果是同步開銷大大增加。

總之,在單執行緒的情況下,就不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作,沒有因為可能出現死鎖而導致的效能消耗。

CPU消耗:

採用單執行緒,避免了不必要的上下文切換和競爭條件,也不存在多程式或者多執行緒導致的切換而消耗 CPU。

但是如果CPU成為Redis瓶頸,或者不想讓伺服器其他CUP核閒置,那怎麼辦?

可以考慮多起幾個Redis程式,Redis是key-value資料庫,不是關聯式資料庫,資料之間沒有約束。只要客戶端分清哪些key放在哪個Redis程式上就可以了。


3.過期鍵的刪除策略

我們都知道,Redis是key-value資料庫,我們可以設定Redis中快取的key的過期時間。Redis的過期策略就是指當Redis中快取的key過期了,Redis如何處理。

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

  1. 定時過期:每個設定過期時間的key都需要建立一個定時器,到過期時間就會立即清除。該策略可以立即清除過期的資料,對記憶體很友好;但是會佔用大量的CPU資源去處理過期的資料,從而影響快取的響應時間和吞吐量。
  2. 惰性過期:只有當訪問一個key時,才會判斷該key是否已過期,過期則清除。該策略可以最大化地節省CPU資源,卻對記憶體非常不友好。極端情況可能出現大量的過期key沒有再次被訪問,從而不會被清除,佔用大量記憶體。
  3. 定期過期:每隔一定的時間,會掃描一定數量的資料庫的expires字典中一定數量的key,並清除其中已過期的key。該策略是前兩者的一個折中方案。通過調整定時掃描的時間間隔和每次掃描的限定耗時,可以在不同情況下使得CPU和記憶體資源達到最優的平衡效果。

(expires字典會儲存所有設定了過期時間的key的過期時間資料,其中,key是指向鍵空間中的某個鍵的指標,value是該鍵的毫秒精度的UNIX時間戳表示的過期時間。鍵空間是指該Redis叢集中儲存的所有鍵。)

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


4.Redis的記憶體淘汰策略有哪些

Redis的記憶體淘汰策略是指在Redis的用於快取的記憶體不足時,怎麼處理需要新寫入且需要申請額外空間的資料。

全域性的鍵空間選擇性移除

  • noeviction:當記憶體不足以容納新寫入資料時,新寫入操作會報錯。

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

  • allkeys-random:當記憶體不足以容納新寫入資料時,在鍵空間中,隨機移除某個key。

設定過期時間的鍵空間選擇性移除

  • volatile-lru:當記憶體不足以容納新寫入資料時,在設定了過期時間的鍵空間中,移除最近最少使用的key。

  • volatile-random:當記憶體不足以容納新寫入資料時,在設定了過期時間的鍵空間中,隨機移除某個key。

  • volatile-ttl:當記憶體不足以容納新寫入資料時,在設定了過期時間的鍵空間中,有更早過期時間的key優先移除。


5. RedLock

Redis 官方站提出了一種權威的基於 Redis 實現分散式鎖的方式名叫 Redlock,此種方式比原先的單節點的方法更安全。它可以保證以下特性:

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

  2. 避免死鎖:最終 client 都可能拿到鎖,不會出現死鎖的情況,即使原本鎖住某資源的 client crash 了或者出現了網路分割槽。

  3. 容錯性:只要大部分 Redis 節點存活就可以正常提供服務。


6. Redis快取異常--快取雪崩

快取雪崩是指快取同一時間大面積的失效,所以,後面的請求都會落到資料庫上,造成資料庫短時間內承受大量請求而崩掉。

解決方案

  1. 快取資料的過期時間設定隨機,防止同一時間大量資料過期現象發生。

  2. 一般併發量不是特別多的時候,使用最多的解決方案是加鎖排隊。

  3. 給每一個快取資料增加相應的快取標記,記錄快取的是否失效,如果快取標記失效,則更新資料快取。


7. Redis快取異常--快取穿透

快取穿透是指快取和資料庫中都沒有的資料,導致所有的請求都落到資料庫上,造成資料庫短時間內承受大量請求而崩掉。

解決方案

  1. 介面層增加校驗,如使用者鑑權校驗,id做基礎校驗,id<=0的直接攔截;

  2. 從快取取不到的資料,在資料庫中也沒有取到,這時也可以將key-value對寫為key-null,快取有效時間可以設定短點,如30秒(設定太長會導致正常情況也沒法使用)。這樣可以防止攻擊使用者反覆用同一個id暴力攻擊

  3. 採用布隆過濾器,將所有可能存在的資料雜湊到一個足夠大的 bitmap 中,一個一定不存在的資料會被這個 bitmap 攔截掉,從而避免了對底層儲存系統的查詢壓力

附加

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

Bitmap: 典型的就是雜湊表

缺點是:Bitmap對於每個元素只能記錄1bit資訊,如果還想完成額外的功能,恐怕只能靠犧牲更多的空間、時間來完成了。

布隆過濾器(推薦)

就是引入了k(k>1)k(k>1)個相互獨立的雜湊函式,保證在給定的空間、誤判率下,完成元素判重的過程。

它的優點是空間效率和查詢時間都遠遠超過一般的演算法,缺點是有一定的誤識別率和刪除困難。

Bloom-Filter演算法的核心思想就是利用多個不同的Hash函式來解決“衝突”。

Hash存在一個衝突(碰撞)的問題,用同一個Hash得到的兩個URL的值有可能相同。為了減少衝突,我們可以多引入幾個Hash,如果通過其中的一個Hash值我們得出某元素不在集合中,那麼該元素肯定不在集合中。只有在所有的Hash函式告訴我們該元素在集合中時,才能確定該元素存在於集合中。這便是Bloom-Filter的基本思想。

Bloom-Filter一般用於在大資料量的集合中判定某元素是否存在。


8. Redis快取異常--快取擊穿

快取擊穿是指快取中沒有但資料庫中有的資料(一般是快取時間到期),這時由於併發使用者特別多,同時讀快取沒讀到資料,又同時去資料庫去取資料,引起資料庫壓力瞬間增大,造成過大壓力。和快取雪崩不同的是,快取擊穿指併發查同一條資料,快取雪崩是不同資料都過期了,很多資料都查不到從而查資料庫。

解決方案

  1. 設定熱點資料永遠不過期。

  2. 加互斥鎖,互斥鎖。


9. 快取預熱

快取預熱就是系統上線後,將相關的快取資料直接載入到快取系統。這樣就可以避免在使用者請求的時候,先查詢資料庫,然後再將資料快取的問題!使用者直接查詢事先被預熱的快取資料!

解決方案

  1. 直接寫個快取重新整理頁面,上線時手工操作一下;

  2. 資料量不大,可以在專案啟動的時候自動進行載入;

  3. 定時重新整理快取;


10.如何保證資料庫和快取雙寫一致性

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

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

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

還有一種方式就是可能會暫時產生不一致的情況,但是發生的機率特別小,就是先更新資料庫,然後再刪除快取。

問題場景 描述 解決
先寫快取,再寫資料庫,快取寫成功,資料庫寫失敗 快取寫成功,但寫資料庫失敗或者響應延遲,則下次讀取(併發讀)快取時,就出現髒讀 這個寫快取的方式,本身就是錯誤的,需要改為先寫資料庫,把舊快取置為失效;讀取資料的時候,如果快取不存在,則讀取資料庫再寫快取
先寫資料庫,再寫快取,資料庫寫成功,快取寫失敗 寫資料庫成功,但寫快取失敗,則下次讀取(併發讀)快取時,則讀不到資料 快取使用時,假如讀快取失敗,先讀資料庫,再回寫快取的方式實現
需要快取非同步重新整理 指資料庫操作和寫快取不在一個操作步驟中,比如在分散式場景下,無法做到同時寫快取或需要非同步重新整理(補救措施)時候 確定哪些資料適合此類場景,根據經驗值確定合理的資料不一致時間,使用者資料重新整理的時間間隔

11.假如Redis裡面有1億個key,其中有10w個key是以某個固定的已知的字首開頭的,如果將它們全部找出來?

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

對方接著追問:如果這個redis正在給線上的業務提供服務,那使用keys指令會有什麼問題?

這個時候你要回答redis關鍵的一個特性:redis的單執行緒的。keys指令會導致執行緒阻塞一段時間,線上服務會停頓,直到指令執行完畢,服務才能恢復。這個時候可以使用scan指令,scan指令可以無阻塞的提取出指定模式的key列表,但是會有一定的重複概率,在客戶端做一次去重就可以了,但是整體所花費的時間會比直接用keys指令長。


四、白日夢的Redis筆記。

1、白日夢的Redis筆記基礎篇:6分鐘看完Redis的八種資料型別

2、MySQL的修仙之路,圖文談談如何學MySQL、如何進階!

相關文章