數據庫面試簡答、30道高頻面試題


目錄


一、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、白日夢的Redis筆記進階篇:萬字長文-整理Redis,各種知識點,建議收藏


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM