資料庫面試簡答、30道高頻面試題
- 一、MySQL問答
- 1、資料庫sql語句查詢,跨表查詢有哪幾種方式
- 2、資料庫的索引用到的是什麼數據結構?
- 3、mylsam、innodb的區別
- 4、MySQL的Gtid複製原理是什麼?
- 5、同步、半同步、非同步複製原理是什麼?
- 6、說說你了解的MySQL慢查詢?
- 7、說說MySQL的執行計劃
- 8、說說MySQL支援的數據類型
- 9. 了解資料庫如何備份嗎
- 10. Oracle和Mysql的區別
- 11. 事務的四種特性
- 12. 四種隔離級別以及什麼是臟讀,幻讀,不可重複讀
- 13. MySQL中 主鍵索引、普通索引、唯一索引的區別
- 14. 資料庫三大範式
- 15. sql語句各種條件的執行順序,如select, where, order by, group by
- 16. 求表的size,或做數據統計可用什麼存儲引擎
- 17. 讀多寫少可用什麼引擎
- 18. 假如要統計多個表應該用什麼引擎
- 19.MySQL Explain各欄位意思
- 20.索引設計的原則?
- 21. MySQL有關許可權的表有哪幾個?
- 二、白日夢的MySQL專題
- 三、Redis問答
- 四、白日夢的Redis筆記。
一、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?原理?
//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
推薦閱讀://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 類型不會被空格填補,但長於指示器的值仍然會被截短。
//www.cnblogs.com/liangxiaofeng/p/5806874.html
9. 了解資料庫如何備份嗎
參考: //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的區別
宏觀上:
-
Mysql是小型資料庫, 開源, 免費, Oracle收費
-
Oracle支援大並發, 大訪問量
-
MySql中安裝後佔用的記憶體小, Oracle不僅佔用記憶體大, 而且越用越大
微觀上:
-
Mysql對事務默認不支援, 但是它的存儲引擎 InnoDB支援事務, Oracle對事務完全支援
-
並發性: MySQL早期的數據引擎MyISAM是支援表級鎖, 後來的InnoDB才支援行級鎖, Oracle支援行級鎖
-
Oracle會將提交的sql寫入連接日誌中, 然後寫入磁碟, 保證不會丟失數據, MySql在執行更新的操作時可能會丟失數據
-
隔離級別不同:
a. Oracle默認使用 read commited 讀已經提交
b. MySQL默認使用的是 repeatable read 可重複讀
-
提交方式
a. Oracle 默認不會自動提交事務
b. MySQL默認自動提交事務
-
邏輯備份
a. Mysql 的數據備份會鎖定數據, 影響正常的DML
b. Oracle在數據備份時, 不會鎖定任何數據
-
數據插入
a. Mysql會更加靈活一點, 比如limit分頁, insert插入多行數據
b. Oracle的分頁使用偽列+子查詢實現 , 插入數據也只能一行行插入
-
許可權控制:
a. Oracle的許可權控制是中規中矩的, 和系統用戶無關
b. MySQL的許可權控制和主機相關, 感覺沒啥意義
-
性能診斷
a. Oracle 有大量的性能診斷工具, 可以實現自動分析
b. Mysql性能診斷方法很少, 主要就是通過通過慢查詢日誌去排查
-
分區表和分區索引
a. Oracle的分區表和分區索引相對來說比較成熟
b. Mysql 分區表和分區索引就不成熟
-
數據複製
a. 在搭建的主從複製的模式中, 主庫出現了問題, 可能會導致從庫有一定數據的丟失, 需要手動的切換的到主庫
b. Oracle 則更強大, 既有傳統的推/拉式的數據複製, 同時也有 dataguard雙機或者多機的容災機制, 而且主庫出現問題, 自動切換到備庫, 但是配置相對複雜
11. 事務的四種特性
ACID:
-
Atomic 原子性: 事務不能被分割, 要麼都做, 要麼都不做。
-
Consistency 一致性: 可以用轉賬的例子解釋一致性。
-
Isolation 隔離性 : 不同的事務, 彼此隔離, 互不干擾。
-
Durability 持久性: 也叫做用就行, 事務一旦被提交, 對資料庫做出的修改將被持久化 。
12. 四種隔離級別以及什麼是臟讀,幻讀,不可重複讀
-
read uncommitted 讀未提交: 在事務A中讀取到了事務B中未提交的數據, 也叫做臟讀。
-
read commited 讀已提交: Oracle默認使用的隔離級別, 讀已提交, 說白了, 事務A先開啟, 然後事務B再開啟, 然後事務Bcommit一個事務操作, 修改數據 , 那麼這個修改是能被事務A讀取到的, 這就叫做讀已提交, 也是所謂的不可重複讀,(因為重複讀之後, 數據可能會發生變化)。
-
repeatable read : 可重複讀, 這也是Mysql默認的事務隔離級別, 事務A開啟後, 無論讀取多少次, 得到的結果都和第一次得到的結果是一樣的, 但是如果事務B在事務A第一次讀取的範圍內插入了一條數據的話, 會發生幻讀, 兩次讀取結果又不一致了, Mysql的InnoDB引擎通過多版本並發控制MVCC解決了這個問題。
-
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.索引設計的原則?
-
適合索引的列是出現在where子句中的列,或者連接子句中指定的列。
-
基數較小的類,索引效果較差,沒有必要在此列建立索引。
-
使用短索引,如果對長字元串列進行索引,應該指定一個前綴長度,這樣能夠節省大量索引空間。
-
不要過度索引。索引需要額外的磁碟空間,並降低寫操作的性能。在修改表內容的時候,索引會進行更新甚至重構,索引列越多,這個時間就會越長。所以只保持需要的索引有利於查詢即可。
21. MySQL有關許可權的表有哪幾個?
表名 | 含義 |
---|---|
user許可權表 | 記錄允許連接到伺服器的用戶帳號資訊,裡面的許可權是全局級的。 |
db許可權表 | 記錄各個帳號在各個資料庫上的操作許可權。 |
table_priv許可權表 | 記錄數據表級的操作許可權。 |
columns_priv許可權表 | 記錄數據列級的操作許可權。 |
host許可權表 | 配合db許可權表對給定主機上資料庫級操作許可權作更細緻的控制。這個許可權表不受GRANT和REVOKE語句的影響。 |
二、白日夢的MySQL專題
4、能談談year、date、datetime、time、timestamp的區別嗎?
9、用 11 張圖講清楚,當你CRUD時BufferPool中發生了什麼!以及BufferPool的優化!
14、簡述undo log、truncate、以及undo 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如何處理。
過期策略通常有以下三種:
- 定時過期:每個設置過期時間的key都需要創建一個定時器,到過期時間就會立即清除。該策略可以立即清除過期的數據,對記憶體很友好;但是會佔用大量的CPU資源去處理過期的數據,從而影響快取的響應時間和吞吐量。
- 惰性過期:只有當訪問一個key時,才會判斷該key是否已過期,過期則清除。該策略可以最大化地節省CPU資源,卻對記憶體非常不友好。極端情況可能出現大量的過期key沒有再次被訪問,從而不會被清除,佔用大量記憶體。
- 定期過期:每隔一定的時間,會掃描一定數量的資料庫的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,此種方式比原先的單節點的方法更安全。它可以保證以下特性:
-
安全特性:互斥訪問,即永遠只有一個 client 能拿到鎖。
-
避免死鎖:最終 client 都可能拿到鎖,不會出現死鎖的情況,即使原本鎖住某資源的 client crash 了或者出現了網路分區。
-
容錯性:只要大部分 Redis 節點存活就可以正常提供服務。
6. Redis快取異常–快取雪崩
快取雪崩是指快取同一時間大面積的失效,所以,後面的請求都會落到資料庫上,造成資料庫短時間內承受大量請求而崩掉。
解決方案
-
快取數據的過期時間設置隨機,防止同一時間大量數據過期現象發生。
-
一般並發量不是特別多的時候,使用最多的解決方案是加鎖排隊。
-
給每一個快取數據增加相應的快取標記,記錄快取的是否失效,如果快取標記失效,則更新數據快取。
7. Redis快取異常–快取穿透
快取穿透是指快取和資料庫中都沒有的數據,導致所有的請求都落到資料庫上,造成資料庫短時間內承受大量請求而崩掉。
解決方案
-
介面層增加校驗,如用戶鑒權校驗,id做基礎校驗,id<=0的直接攔截;
-
從快取取不到的數據,在資料庫中也沒有取到,這時也可以將key-value對寫為key-null,快取有效時間可以設置短點,如30秒(設置太長會導致正常情況也沒法使用)。這樣可以防止攻擊用戶反覆用同一個id暴力攻擊
-
採用布隆過濾器,將所有可能存在的數據哈希到一個足夠大的 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快取異常–快取擊穿
快取擊穿是指快取中沒有但資料庫中有的數據(一般是快取時間到期),這時由於並發用戶特別多,同時讀快取沒讀到數據,又同時去資料庫去取數據,引起資料庫壓力瞬間增大,造成過大壓力。和快取雪崩不同的是,快取擊穿指並發查同一條數據,快取雪崩是不同數據都過期了,很多數據都查不到從而查資料庫。
解決方案
-
設置熱點數據永遠不過期。
-
加互斥鎖,互斥鎖。
9. 快取預熱
快取預熱就是系統上線後,將相關的快取數據直接載入到快取系統。這樣就可以避免在用戶請求的時候,先查詢資料庫,然後再將數據快取的問題!用戶直接查詢事先被預熱的快取數據!
解決方案
-
直接寫個快取刷新頁面,上線時手工操作一下;
-
數據量不大,可以在項目啟動的時候自動進行載入;
-
定時刷新快取;
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、如何進階!