每日八股:MySQL與Redis
MySQL
MYSQL引擎
1.InnoDB:MySQL默認存儲引擎,具有ACID事務支持、行級鎖、外鍵約束等特性,適用于高并發(fā)的讀寫操作,支持較好的數(shù)據(jù)完整性和并發(fā)操作。
2.MyiSAM:MySQL的另一個存儲引擎,不支持事務、行級鎖和外鍵約束,適用于大量讀操作的場景,在并發(fā)寫入和數(shù)據(jù)完整性方面有一定的限制。
3.Memory:將事務存儲在內(nèi)存中,適用于對性能要求較高的讀操作,可能會造成數(shù)據(jù)丟失,不支持事務、行級鎖和外鍵約束。
MySQL為什么InnoDB是默認引擎
因為InnoDB在事務支持、并發(fā)性能、崩潰恢復等方面具有優(yōu)勢。
mysql的innodb與MyISAM的區(qū)別?
1.事務:innodb支持事務,MyISAM不支持事務。
2.索引結(jié)構(gòu):innodb是聚簇索引,MyISAM是非聚簇索引。
3.鎖粒度:innodb的最小鎖粒度是行鎖,MyISAM的最小鎖粒度是表鎖。
4.count的效率:innodb不保存表的具體行數(shù),執(zhí)行count函數(shù)需要掃描整個表,MyISAM保存表的具體行數(shù),執(zhí)行速度更快。
Redis
Redis的底層數(shù)據(jù)結(jié)構(gòu)
1.String:可以是字符串、整數(shù)或浮點數(shù)。
2.List:鏈表,鏈表上的每個節(jié)點都包含一個字符串。
3.Set:無序集合
4.Hash:無序散列表
5.Zset:有序集合
Zset底層是怎么實現(xiàn)的?
Zset底層數(shù)據(jù)結(jié)構(gòu)通過壓縮列表或跳表實現(xiàn)。 如果Zset的元素個數(shù)少于128個,每個元素的值小于64個字節(jié)時,使用壓縮列表,否則使用跳表。
介紹一下listpack
listpack相較于壓縮列表最大的特點是每個節(jié)點中不再存儲上一個節(jié)點的長度,壓縮列表中正是因為每個節(jié)點包含了前一個節(jié)點的長度信息,導致存在連鎖更新的隱患。listpack還是用一塊連續(xù)的內(nèi)存空間來緊湊的保存數(shù)據(jù),并且為了節(jié)省內(nèi)存的開銷,listpack節(jié)點會采用不同的編碼方式來存儲不同大小的節(jié)點數(shù)據(jù)。
Redis中哈希表是怎么進行擴容的?
Redis采用漸進式rehash。在rehash時,會用到兩個哈希表(哈希表1和哈希表2),步驟如下:
1.給哈希表2分配空間
2.在rehash期間,每次哈希表元素進行增刪改查等操作時,Redis除了執(zhí)行對應的操作外,還會順序?qū)⒐1?中索引位置上的所有鍵值對信息轉(zhuǎn)移到哈希表2中
3.隨著處理客戶端發(fā)起的哈希表操作請求數(shù)量越多,最終在某個時間點會將哈希表1中的全部鍵值對信息都遷移到哈希表2中,從而完成rehash操作。
哈希表擴容的時候,有讀請求怎么查?
在哈希表擴容的時候,查找一個key值的話,會先去哈希表1中進行查找,如果沒有找到,再去哈希表2中進行查找。
Redis為什么快?
1.Redis大部分操作都是在內(nèi)存中完成的,并且采用了高效的數(shù)據(jù)結(jié)構(gòu),因此Redis的瓶頸可能是內(nèi)存或者網(wǎng)絡帶寬,而不是cpu,既然cpu不是瓶頸,那就自然采用單線程的解決方案了。
2.Redis采用單線程模型可以避免多線程之間的競爭,省去了多線程切換帶來的時間和性能上的開銷,同時也不會導致死鎖問題。
3.Redis采用了I/O多路復用機制來處理大量的客戶端socket請求。
如何實現(xiàn)redis原子性?
1.redis執(zhí)行一條命令的時候是具備原子性的,因為redis是單線程執(zhí)行的,不存在多線程安全的問題。如果要保證兩條命令的原子性,可以考慮使用Lua腳本,將多個操作寫到一個Lua腳本中,redis會把整個Lua腳本當成一個整體來執(zhí)行,執(zhí)行過程中不會被其他命令打斷,從而保證了Lua腳本中操作的原子性。
2.redis中事務也能保證原子性。若redis事務正常執(zhí)行,可以保證原子性;redis事務執(zhí)行過程中某一個操作執(zhí)行失敗,不保證原子性。
Redis的持久化方式
1.AOF日志:每執(zhí)行一條寫操作命令,就將該命令以追加的方式寫入到一個文件里。
優(yōu)點:提供了更好的安全性,因為它默認將每一次寫操作都追加到文件末尾,即使redis發(fā)生了故障,也只會丟失最后一次寫入前的數(shù)據(jù)。
缺點:因為記錄了每一個寫操作,所以AOF占用的磁盤空間大。當redis發(fā)生故障之后進行重寫時,若AOF文件體積過大,可能會對redis寫入操作性能造成一定的影響,因為它需要重放所有的寫操作。
2.RDB快照:將某一時刻的內(nèi)存數(shù)據(jù),以二進制的方式寫入磁盤。
優(yōu)點:RDB通過快照的形式保存某一時刻的數(shù)據(jù)狀態(tài),文件體積小,備份和恢復的速度快,不會對redis的性能造成很大的影響。
缺點:RDB方式在兩次快照之間,如果redis服務器發(fā)生了故障,那么這段時間內(nèi)的數(shù)據(jù)將全部丟失。并且,如果在RDB創(chuàng)建快照到恢復期間有寫操作,恢復后的數(shù)據(jù)可能與故障前的數(shù)據(jù)不一致。
過期刪除策略和內(nèi)存淘汰策略的區(qū)別
1.內(nèi)存淘汰策略是指內(nèi)存要滿了的時候,redis回觸發(fā)內(nèi)存淘汰策略,淘汰一些不必要的內(nèi)存資源,以騰出空間,保存新的內(nèi)容。
其中內(nèi)存淘汰策略可分為不進行數(shù)據(jù)淘汰的策略和進行數(shù)據(jù)淘汰的策略。
不進行數(shù)據(jù)淘汰的策略:表示當運行內(nèi)存超過最大設置內(nèi)存時,不淘汰任何數(shù)據(jù),這時如果有數(shù)據(jù)寫入,會報錯禁止數(shù)據(jù)寫入,對于單純的查詢或刪除操作可以正常執(zhí)行。
進行數(shù)據(jù)淘汰的策略:可細分為在設置了過期時間的數(shù)據(jù)中進行淘汰和在所有數(shù)據(jù)范圍內(nèi)進行淘汰。
2.過期刪除策略是指將已過期的鍵值對信息進行刪除,redis中采用的過期刪除策略是惰性刪除+定期刪除。
Redis的緩存失效會不會立即刪除?
不會,Redis的過期刪除策略是采用惰性刪除+定期刪除這兩種策略配合使用的。
惰性刪除策略的做法是:不會主動刪除過期鍵,每次從數(shù)據(jù)庫訪問key時,都會檢查key是否過期,如果過期則刪除。
定期刪除策略的做法是:每隔一段時間都會隨機的從數(shù)據(jù)庫中抽取一定數(shù)量的key,檢查它們是否過期,刪除其中已過期的key。
redis主從和集群可以保證數(shù)據(jù)一致性嗎?
redis主從和集群都屬于AP模型,即在面臨網(wǎng)絡分區(qū)時選擇保證可用性和分區(qū)容忍性,而犧牲了強一致性。這意味著在網(wǎng)絡分區(qū)的情況下,redis主從復制和集群可以繼續(xù)提供服務并保持可用,但可能會出現(xiàn)部分節(jié)點之間的數(shù)據(jù)不一致。
哨兵機制說一下
哨兵機制:它的作用是實現(xiàn)主從節(jié)點故障轉(zhuǎn)移。它會監(jiān)測主節(jié)點是否存活,如果發(fā)現(xiàn)主節(jié)點掛了,它會立即選舉一個從節(jié)點切換為主節(jié)點,并且把新主節(jié)點的信息傳遞給從節(jié)點和客戶端。哨兵節(jié)點主要負責三件事:監(jiān)控、選主、通知。
哨兵機制的選主節(jié)點的算法
1.故障節(jié)點主觀下線
2.故障節(jié)點客觀下線
3.Sentinel集群選舉Leader
4.Sentinel Leader決定新主節(jié)點
Redis集群模式以及優(yōu)缺點
當Redis緩存數(shù)據(jù)量大到一臺服務器無法緩存時,就需要使用Redis分片集群方案,它將數(shù)據(jù)分布在不同的服務器上,降低了系統(tǒng)對單主節(jié)點的依賴,從而提高了redis的讀寫性能。
優(yōu)點:高可用性、高性能、擴展性好。
缺點:部署和維護較復雜、集群同步問題、數(shù)據(jù)分片限制。
本地緩存和Redis緩存的區(qū)別?
1.本地緩存是指將數(shù)據(jù)存儲在本地應用程序或服務器上,通常用于加速數(shù)據(jù)訪問和提高響應速度,本地緩存使用內(nèi)存作為存儲介質(zhì),利用內(nèi)存高速讀寫的特性來提高數(shù)據(jù)訪問速度。
優(yōu)點:訪問速度快、減輕網(wǎng)絡壓力、低延遲。
缺點:可擴展性有限。
2.Redis緩存是指將數(shù)據(jù)存儲在多個分布式節(jié)點上,通過協(xié)同工作來提供高性能的數(shù)據(jù)訪問服務。分布式存儲通常使用集群方式進行部署,利用多臺服務器來分擔數(shù)據(jù)訪問和存儲的壓力。
優(yōu)點:可擴展性強、數(shù)據(jù)一致性高、易于維護。
缺點:訪問速度慢、網(wǎng)絡開銷大。
Redis的應用場景
緩存、分布式鎖、排行榜、計數(shù)器、消息隊列。
Redis的大Key問題是什么?
redis的大Key問題指的是某個key對應的value值在內(nèi)存中占用空間過大,導致redis的性能下降、內(nèi)存不足、數(shù)據(jù)不均衡以及主從同步延遲等問題。
大Key問題的缺點
內(nèi)存占用過高、性能下降、阻塞其他操作、網(wǎng)絡擁塞、主從同步延遲、數(shù)據(jù)傾斜。
Redis中大Key如何解決?
1.對大Key進行拆分
2.對大Key進行清理
3.監(jiān)控Redis的內(nèi)存水位
4.對過期數(shù)據(jù)進行定期清理
什么是熱Key?
通常以其接收到的Key被請求頻率來判定:
1.QPS集中在特定的Key
2.帶寬使用率集中在特定的Key
3.CPU使用時間占比集中在特定的Key
如何解決熱Key問題?
1.在Redis集群架構(gòu)中對熱Key進行復制
2.使用讀寫分離架構(gòu)
緩存雪崩、擊穿、穿透是什么?怎么解決?
1.緩存雪崩:當大量緩存數(shù)據(jù)在同一時間過期(失效)或Redis故障宕機時,如果此時有大量的用戶請求,都無法在Redis中處理,于是全部請求直接訪問數(shù)據(jù)庫,就會使數(shù)據(jù)庫的壓力驟增,嚴重的會導致數(shù)據(jù)庫宕機,從而引發(fā)一系列的連鎖反應,造成系統(tǒng)崩潰。
解決方案:
①均勻設置過期時間:如果要給緩存數(shù)據(jù)加上一個過期時間,應避免大量數(shù)據(jù)設置成同一個過期時間。我們可以在對緩存數(shù)據(jù)設置過期時間時,給這些數(shù)據(jù)的過期時間加上一個隨機數(shù),這樣就保證數(shù)據(jù)不會在同一時間過期。
②互斥鎖:當業(yè)務線程在處理用戶請求時,如果發(fā)現(xiàn)訪問的數(shù)據(jù)不在redis里,就加一個互斥鎖,保證同一時間內(nèi)只有一個請求來構(gòu)建緩存(從數(shù)據(jù)庫中讀取數(shù)據(jù),再將數(shù)據(jù)更新到redis),當構(gòu)建緩存完成后,再釋放鎖。
③后臺更新緩存:業(yè)務線程不再負責更新緩存,也不為緩存設置有效期,而是讓緩存永久有效,將更新緩存的工作交給后臺線程定時更新。
2.緩存擊穿:如果緩存中的某個熱點數(shù)據(jù)過期了,此時大量的請求訪問了該熱點數(shù)據(jù),都無法從緩存中讀取,直接訪問數(shù)據(jù)庫,數(shù)據(jù)庫很容易就被高并發(fā)的請求沖垮。
解決方案:
①互斥鎖方案:保證同一時間內(nèi)只有一個業(yè)務線程更新緩存,未獲取到互斥鎖的請求,要么等待鎖釋放后重新讀取緩存,要么就返回空值或默認值。
②不給熱點數(shù)據(jù)設置過期時間,由后臺異步更新緩存,或在熱點數(shù)據(jù)即將過期時,提前通知后臺線程更新緩存以及重新設置過期時間。
3.緩存穿透:當用戶訪問的數(shù)據(jù),即不在緩存中,也不在數(shù)據(jù)庫中,導致請求在訪問緩存時,發(fā)現(xiàn)緩存缺失數(shù)據(jù),再去訪問數(shù)據(jù)庫時,發(fā)現(xiàn)數(shù)據(jù)庫中也沒有要訪問的數(shù)據(jù),無法構(gòu)建緩存數(shù)據(jù),來服務后續(xù)的請求。那么當有大量這樣的請求到來時,數(shù)據(jù)庫的壓力驟增。
解決方案:
①非法請求的限制:當有大量惡意的請求訪問不存在的數(shù)據(jù)時,也會發(fā)生緩存穿透,因此在API的入口處應判斷請求參數(shù)是否合理,請求參數(shù)是否含有非法值,請求字段是否存在,如果發(fā)現(xiàn)是惡意請求就直接返回錯誤,避免進一步訪問緩存和數(shù)據(jù)庫。
②緩存空值或默認值:當我們線上業(yè)務發(fā)現(xiàn)緩存穿透現(xiàn)象時,可以針對查詢的數(shù)據(jù),在緩存中設置一個空值或默認值,這樣后續(xù)請求就可以從緩存中獲取到空值或默認值返回給應用,而不會繼續(xù)查詢數(shù)據(jù)庫。
③布隆過濾器:我們可以在寫入數(shù)據(jù)時,使用布隆過濾器做個標記,當業(yè)務線程確認緩存失效后,可以通過布隆過濾器判斷數(shù)據(jù)是否存在,如果不存在,就不用通過數(shù)據(jù)庫來判斷數(shù)據(jù)是否存在。即使發(fā)生了緩存穿透,大量請求也只會訪問redis和布隆過濾器,而不會查詢數(shù)據(jù)庫,保證了數(shù)據(jù)庫的正常運行。
布隆過濾器的原理
布隆過濾器由初始值都為0的位圖數(shù)組和N個哈希函數(shù)構(gòu)成。當我們在寫入數(shù)據(jù)庫數(shù)據(jù)時,在布隆過濾器里做個標記,這樣下次在查詢數(shù)據(jù)是否在數(shù)據(jù)庫中時,只需要查詢布隆過濾器,如果數(shù)據(jù)沒有被標記,說明數(shù)據(jù)不在數(shù)據(jù)庫中。布隆過濾器會通過下面三個步驟完成:
1.使用N個哈希函數(shù)分別對數(shù)據(jù)進行哈希運算,得到N個哈希值
2.將第一步得到的N個哈希值對位圖數(shù)組的長度取模,得到每個哈希值在位圖數(shù)組中的對應位置
3.將每個哈希值在位圖數(shù)組中對應位置的值都設為1
當應用要查詢數(shù)據(jù)是否在數(shù)據(jù)庫中時,通過布隆過濾器查詢位圖數(shù)組中對應位置的值是否全為1,只要有一個為0,說明該數(shù)據(jù)不在數(shù)據(jù)庫中。布隆過濾器由于是基于哈希函數(shù)實現(xiàn)的,高效查找的同時會存在哈希沖突的可能性,可能會存在多個數(shù)據(jù)處于位圖數(shù)組的同一位置的情況從而導致誤判。所以,查詢布隆過濾器說數(shù)據(jù)存在,并不一定數(shù)據(jù)就存在于數(shù)據(jù)庫中,但若查詢數(shù)據(jù)不存在,則數(shù)據(jù)庫中一定不存在這個數(shù)據(jù)。
#八股##面試常問題系列##Java選手#