抱佛腳一時爽挑宠,一直抱佛腳一直爽蚁鳖!這篇文章總結常見的數(shù)據(jù)庫面試問題~因為是抱佛腳曹质,所以結構上沒有什么邏輯...
事務
ACID屬性
原子性(Atomicity):操作要么全部被執(zhí)行骆撇,要么都不執(zhí)行
一致性(Consistency):數(shù)據(jù)庫在事務執(zhí)行前后都保持一致性狀態(tài)。在一致性狀態(tài)下父叙,所有事務對同一個數(shù)據(jù)的讀取結果都是相同的
隔離性(Isolation):多個事務并發(fā)執(zhí)行時神郊,一個事務的執(zhí)行不應影響其他事務的執(zhí)行
持久性(Durability):已被提交的事務對數(shù)據(jù)庫的修改應該永久保存在數(shù)據(jù)庫中,以應對系統(tǒng)崩潰的情況
嵌套事務
嵌套是子事務套在父事務中執(zhí)行高每,子事務是父事務的一部分屿岂,在進入子事務之前践宴,父事務建立一個回滾點鲸匿,叫save point,然后執(zhí)行子事務阻肩,這個子事務的執(zhí)行也算是父事務的一部分带欢,然后子事務執(zhí)行結束,父事務繼續(xù)執(zhí)行
若子事務回滾烤惊,則:父事務會回滾到進入子事務前建立的save point乔煞,然后嘗試其他的事務或者其他的業(yè)務邏輯,父事務之前的操作不會受到影響柒室,更不會自動回滾
若父事務回滾渡贾,則:子事務也會跟著回滾,因為是子事務先提交雄右,父事務再提交
并發(fā)一致性問題
丟失修改:一個事務的更新操作被另外一個事務的更新操作覆蓋
臟讀:T1 修改一個數(shù)據(jù)但未提交空骚,T2 隨后讀取這個數(shù)據(jù)纺讲。如果 T1 撤銷了這次修改,那么 T2 讀取的數(shù)據(jù)是臟數(shù)據(jù)
不可重復讀:T2 讀取一個數(shù)據(jù)囤屹,T1 對該數(shù)據(jù)做了修改熬甚。如果 T2 再次讀取這個數(shù)據(jù),此時讀取的結果和第一次讀取的結果不同
幻讀:當同一查詢多次執(zhí)行時肋坚,由于其它事務在這個數(shù)據(jù)范圍內(nèi)執(zhí)行了插入操作乡括,會導致每次返回不同的結果集(和不可重復讀的區(qū)別:針對的是一個數(shù)據(jù)整體/范圍;并且需要是插入操作)
并發(fā)控制
-
加鎖
鎖的粒度:封鎖粒度越小智厌,系統(tǒng)并發(fā)性越高诲泌,系統(tǒng)開銷越大
-
鎖的類型
-
讀寫鎖
讀鎖(S鎖、共享鎖):加S鎖后峦剔,可讀取档礁、不可更新,其他事務可以加S鎖
寫鎖(X鎖吝沫、互斥鎖):加X鎖后呻澜,可進行讀取和更新,其他事務不能再加任何鎖
-
意向鎖:IX/IS 都是表鎖惨险,用來表示一個事務想要在表中的某個數(shù)據(jù)行上加 X 鎖或 S 鎖(它們只表示想要對表加鎖羹幸,而不是真正加鎖)
一個事務在獲得某個數(shù)據(jù)行對象的 S 鎖之前,必須先獲得表的 IS 鎖或者更強的鎖(強度:IS<S<IX<X)
一個事務在獲得某個數(shù)據(jù)行對象的 X 鎖之前辫愉,必須先獲得表的 IX 鎖
通過引入意向鎖栅受,事務 T 想要對表 A 加 X 鎖,只需要先檢測是否有其它事務對表 A 加了 X/IX/S/IS 鎖(而不需要一行一行掃是否有行鎖)恭朗,如果加了就表示有其它事務正在使用這個表或者表中某一行的鎖屏镊,因此事務 T 加 X 鎖失敗
事務 T1 想要對數(shù)據(jù)行 R1 加 X 鎖,事務 T2 想要對同一個表的數(shù)據(jù)行 R2 加 X 鎖痰腮,兩個事務都需要對該表加 IX 鎖而芥,但是 IX 鎖是兼容的,并且 IX 鎖與行級的 X 鎖也是兼容的膀值,因此兩個事務都能加鎖成功棍丐,對同一個表中的兩個數(shù)據(jù)行做修改
-
-
三級封鎖協(xié)議
一級封鎖協(xié)議(解決丟失修改問題):事務 T 要修改數(shù)據(jù) A 時必須加 X 鎖,直到 T 結束才釋放鎖
二級封鎖協(xié)議(解決丟失修改+臟讀問題):一級+要求讀取數(shù)據(jù) A 時必須加 S 鎖沧踏,讀取完馬上釋放 S 鎖
三級封鎖協(xié)議(解決丟失修改+臟讀+不可重復讀問題):二級+要求讀取數(shù)據(jù) A 時必須加 S 鎖歌逢,直到事務結束了才能釋放 S 鎖
-
兩段鎖協(xié)議
加鎖和解鎖分為兩個階段進行
符合兩段鎖協(xié)議的:lock-x(A)...lock-s(B)...lock-s(C)...unlock(A)...unlock(C)...unlock(B)
不符合兩段鎖協(xié)議的:lock-x(A)...unlock(A)...lock-s(B)...unlock(B)...lock-s(C)...unlock(C)
符合兩段鎖協(xié)議是可串行化調(diào)度(并發(fā)執(zhí)行的事務結果與以某個順序串行執(zhí)行的事務結果相同)的充分不必要條件
-
mysql加鎖語句
加S鎖:SELECT ... LOCK In SHARE MODE;
加X鎖:SELECT ... FOR UPDATE;
-
隔離級別
未提交讀:事務中的修改,即使沒有提交翘狱,對其它事務也是可見的
提交讀【可解決臟讀問題】:一個事務所做的修改在提交之前對其它事務是不可見的
可重復讀【可解決臟讀+不可重復讀問題】:保證在同一個事務中多次讀取同一數(shù)據(jù)的結果是一樣的秘案;MySQL的默認隔離級別
可串行化【可解決臟讀+不可重復讀+幻讀問題】:通過加鎖強制事務串行執(zhí)行,這樣多個事務互不干擾,不會出現(xiàn)并發(fā)一致性問題
傳播行為
最常用:PROPAGATION_REQUIRED(如果當前沒有事務阱高,就創(chuàng)建一個新事務师骗,如果當前存在事務,就加入該事務)
磁盤讀取的特性
系統(tǒng)從磁盤讀取數(shù)據(jù)到內(nèi)存時是以磁盤塊(block)為基本單位的讨惩,位于同一個磁盤塊中的數(shù)據(jù)會被一次性讀取出來
索引
數(shù)據(jù)結構基礎
二叉查找樹:左子樹的鍵值小于等于根的鍵值辟癌,右子樹的鍵值大于根的鍵值
平衡二叉樹(AVL樹):二叉查找樹+任何節(jié)點的兩個子樹的高度最大差為1
-
平衡多路查找樹(B樹):所有葉子節(jié)點都在同一層
-
B+樹:與B樹的主要區(qū)別:除主鍵外的data全部放在葉子節(jié)點中,非葉子節(jié)點只放主鍵值信息荐捻;所有葉子節(jié)點之間都有雙向指針
-
查找過程:
在根節(jié)點進行二分查找黍少,找到key對應的指針
遞歸地在指針所指向的節(jié)點進行查找,直到查找到葉子節(jié)點
在葉子節(jié)點上進行二分查找处面,找出 key 所對應的 data
-
B+樹相對B樹的優(yōu)勢
磁盤讀寫代價更低:所有數(shù)據(jù)信息全部存儲在葉子節(jié)點里厂置,這樣,整個樹的每個節(jié)點所占的內(nèi)存空間就變小了魂角,讀到內(nèi)存中的索引信息就會更多一些昵济,相當于減少了磁盤IO次數(shù)
查詢效率更加穩(wěn)定:任何關鍵字的查找必須走一條從根結點到葉子結點的路。所有關鍵字查詢的路徑長度相同野揪,導致每一個數(shù)據(jù)的查詢效率相當
方便區(qū)間查詢:B+樹的數(shù)據(jù)都存儲在葉子結點中访忿,分支結點均為索引,方便掃庫斯稳,只需要掃一遍葉子結點即可海铆,但是B樹因為其分支結點同樣存儲著數(shù)據(jù),我們要找到具體的數(shù)據(jù)挣惰,需要進行一次中序遍歷按序來掃
B+樹相對于紅黑樹的優(yōu)勢
更低的樹高:紅黑樹出度為2卧斟,而B+樹出度可以很大
索引類型
-
聚簇索引與非聚簇索引
-
聚簇索引:
葉子節(jié)點 data 域記錄著完整的數(shù)據(jù)記錄
因為無法把數(shù)據(jù)行存放在兩個不同的地方,所以一個表只能有一個聚簇索引
-
非聚簇索引:
葉子節(jié)點的 data 域記錄著主鍵的值
在使用非聚簇索引進行查找時憎茂,需要先查找到主鍵值珍语,然后再到聚簇索引中進行查找
-
-
哈希索引
用拉鏈法解決哈希沖突
哈希索引中只保存哈希值和行指針,不存儲字段值
哈希索引數(shù)據(jù)并非按索引值順序存儲竖幔,所以無法用于排序板乙、只支持等值查詢
哈希索引不支持部分索引列匹配查找,因為它是對一行的所有列算的哈希值
-
自適應哈希索引(innodb)
當某些索引值被頻繁使用時赏枚,Innodb會在B+樹索引的基礎上建立哈希索引
在B+樹上用哈希值而不是鍵本身進行查找
栗子:原本在url這一列上建了B+樹索引亡驰,url被經(jīng)常用在where中晓猛,所以刪除原來url列上的索引饿幅,用url的哈希值建立B+樹索引
好處:①哈希值體積比url小,省空間戒职;②整數(shù)比較比字符串比較快
可以在where語句中指定哈希函數(shù)
-
全文索引(myisam)
用于查找文本中的關鍵詞栗恩,而不是直接比較是否相等
全文索引使用倒排索引實現(xiàn),它記錄著關鍵詞到其所在文檔的映射
查找條件使用 MATCH AGAINST洪燥,而不是普通的 WHERE
-
覆蓋索引
- 索引包含了所有滿足查詢所需要的數(shù)據(jù)磕秤,查詢的時候只需要讀取索引而不需要回表讀取數(shù)據(jù)
索引優(yōu)化
在需要使用多個列作為條件進行查詢時乳乌,使用多列索引比使用多個單列索引性能更好
讓選擇性最強的索引列放在前面(即SELECT COUNT(DISTINCT staff_id)/COUNT(*)越高放越前面)
適當使用覆蓋索引(索引包含所有需要查詢的字段的值):索引通常遠小于數(shù)據(jù)行的大小,只讀取索引能大大減少數(shù)據(jù)訪問量市咆;一些存儲引擎(例如 MyISAM)在內(nèi)存中只緩存索引汉操,而數(shù)據(jù)依賴于操作系統(tǒng)來緩存。因此蒙兰,只訪問索引可以不使用系統(tǒng)調(diào)用
索引的好處
大大減少了服務器需要掃描的數(shù)據(jù)行數(shù)
幫助服務器避免進行排序和分組磷瘤,以及避免創(chuàng)建臨時表(B+Tree 索引是有序的,可以用于 ORDER BY 和 GROUP BY 操作搜变。臨時表主要是在排序和分組過程中創(chuàng)建采缚,不需要排序和分組,也就不需要創(chuàng)建臨時表)
將隨機 I/O 變?yōu)轫樞?I/O(B+Tree 索引是有序的挠他,會將相鄰的數(shù)據(jù)都存儲在一起)
索引何時不會被使用
如果條件中有or扳抽,即使其中有條件帶索引也不會使用
如果mysql估計使用全表掃描要比使用索引快,則不使用索引(比如重復數(shù)據(jù)較多的列)
查詢時,采用is null條件時殖侵,不能利用到索引贸呢,只能全表掃描(因為索引不存null)
前導模糊查詢不能利用索引(like '%XX'或者like '%XX%')
數(shù)據(jù)類型出現(xiàn)隱式轉(zhuǎn)化(如varchar不加單引號的話可能會自動轉(zhuǎn)換為int型)
MySQL
AUTOCOMMIT
MySQL 默認采用自動提交模式:如果不顯式使用START TRANSACTION語句來開始一個事務,那么每個操作都會被當做一個事務并自動提交
加鎖
對select不加鎖 ? 對增刪改加鎖 ? 支持行級拢军、頁級贮尉、表級鎖
如何判斷查詢是否命中了索引
explain語句(EXPALIN只能解釋SELECT操作,其他操作要重寫為SELECT后查看執(zhí)行計劃)
事務日志
存儲引擎在修改表數(shù)據(jù)時朴沿,并不是把修改的數(shù)據(jù)本身持久化到磁盤猜谚,而是在內(nèi)存中修改,并把修改行為記錄到持久化到磁盤上了的事務日志中
事務日志是追加的方式赌渣,因此寫日志操作是磁盤一小塊區(qū)域內(nèi)的順序I/O
事務日志持久后魏铅,內(nèi)存中被修改的數(shù)據(jù)會慢慢持久化到磁盤
MVCC
是InnoDB 存儲引擎實現(xiàn)隔離級別的一種具體方式,用于實現(xiàn)提交讀和可重復讀這兩種隔離級別(未提交讀隔離級別總是讀取最新的數(shù)據(jù)行坚芜,要求很低览芳,無需使用 MVCC;可串行化隔離級別需要對所有讀取的行都加鎖鸿竖,單純使用 MVCC 無法實現(xiàn))
基本思想:寫操作更新最新的版本快照沧竟,而讀操作去讀舊版本快照,沒有互斥關系
-
具體過程
插入操作時缚忧,記錄創(chuàng)建版本號
刪除操作時悟泵,記錄刪除版本號
更新操作時,先記錄刪除版本號闪水,再新增一行記錄創(chuàng)建版本號
查詢操作時糕非,要符合以下條件才能被查詢出來:刪除版本號未定義或大于當前事務版本號(刪除操作是在當前事務啟動之后做的);創(chuàng)建版本號小于或等于當前事務版本號(創(chuàng)建操作是事務完成或者在事務啟動之前完成)
InnoDB
支持外鍵約束:FOREIGN KEY(deptId) REFERENCES tb_dept1(id),則當前表的deptId列中有的值必須在表tb_dept1的id列中存在
有行級鎖定(若不能確定要掃描的范圍朽肥,還是需要鎖全表)
實現(xiàn)了四種隔離級別
不支持FULLTEXT類型的索引禁筏,且不保存表的行數(shù)(所以 select count(*) from table需要掃描全表統(tǒng)計行數(shù))
采用兩段鎖協(xié)議,會根據(jù)隔離級別在需要的時候自動加鎖衡招,并且所有的鎖都是在同一時刻被釋放篱昔,這被稱為隱式鎖定
-
加鎖
意向鎖是 InnoDB 自動加的, 不需用戶干預
對于 UPDATE始腾、 DELETE 和 INSERT 語句旱爆, InnoDB 會自動給涉及數(shù)據(jù)集加排他鎖(X); 對于普通 SELECT 語句窘茁,InnoDB 不會加任何鎖
-
磁盤讀取
InnoDB存儲引擎中頁是其磁盤管理的最小單位怀伦,每次申請磁盤空間時都會是若干地址連續(xù)磁盤塊來達到頁的大小
InnoDB在把磁盤數(shù)據(jù)讀入到磁盤時會以頁為基本單位
-
間隙鎖
innodb通過行鎖和間隙鎖共同組成的next-key lock防止幻讀
對前開后閉的區(qū)間加鎖(假設a b是相鄰兩行的數(shù)據(jù),間隙鎖是對(a, b)加鎖山林,行鎖是對b加鎖房待,所以next-key lock是對(a,b]加鎖)
MyISAM
mysql默認的引擎
支持全文索引
支持表級鎖
InnoDB vs MyISAM
事務:InnoDB 是事務型的,可以使用 Commit 和 Rollback 語句
并發(fā):MyISAM 只支持表級鎖驼抹,而 InnoDB 還支持行級鎖桑孩;InnoDB支持MVCC版本控制
外鍵:InnoDB 支持外鍵
備份:InnoDB 支持在線熱備份(備份時數(shù)據(jù)庫仍可用)
并發(fā):InnoDB 讀寫阻塞與事務隔離級別相關;MyISAM讀寫互相阻塞:不僅會在寫入的時候阻塞讀取框冀,MyISAM還會在讀取的時候阻塞寫入流椒,但讀本身并不會阻塞另外的讀
-
存儲:
InnoDB【索引和數(shù)據(jù)在一塊】:表空間數(shù)據(jù)文件(由數(shù)據(jù)段和索引段組成,數(shù)據(jù)段即為B+樹上的葉子節(jié)點明也;索引段就是B+樹上的非葉子節(jié)點)和它的日志文件
MyISAM【索引和數(shù)據(jù)分開】:表保存為文件的形式(所以在跨平臺的數(shù)據(jù)轉(zhuǎn)移中使用MyISAM存儲會省去不少的麻煩): .frm文件存儲表定義宣虾,數(shù)據(jù)文件的擴展名為.MYD,索引文件的擴展名是.MYI
MyISAM 支持壓縮表和空間數(shù)據(jù)索引温数,InnoDB需要更多的內(nèi)存和存儲
-
索引:
-
InnoDB:
-
主索引(主鍵上的索引):聚簇索引绣硝;因為InnoDB的數(shù)據(jù)文件本身要按主鍵聚集,所以InnoDB要求表必須有主鍵(MyISAM可以沒有)撑刺,如果沒有顯式指定鹉胖,則MySQL系統(tǒng)會自動選擇一個可以唯一標識數(shù)據(jù)記錄的列作為主鍵,如果不存在這種列够傍,則MySQL自動為InnoDB表生成一個隱含字段作為主鍵
-
-
* 輔索引:非聚簇索引甫菠,存主鍵的值;需要再去主索引中查找

* MyISAM:
* 主索引:非聚簇索引冕屯,存地址

* 輔索引:非聚簇索引寂诱,存地址

-
適用場景:
如果應用中需要執(zhí)行大量的INSERT或UPDATE操作\需要使用外鍵約束,則應該使用InnoDB(因為支持4個事務隔離級別愕撰、MVCC刹衫、行級鎖)
如果應用中需要執(zhí)行大量的SELECT查詢,使用MyISAM(因為提供高速存儲和檢索搞挣,以及全文搜索能力)
為什么MyISAM會比Innodb 的查詢速度快
InnoDB 要緩存數(shù)據(jù)和索引带迟,MyISAM只緩存索引塊
innodb輔索引查詢后還要查主索引,MyISAM記錄的直接是文件的OFFSET
InnoDB 還需要維護MVCC一致
SQL
-
返回3-5行
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" cid="n358" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit;" lang="sql">SELECT *
FROM mytable
LIMIT 2, 3;</pre> -
排序
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" cid="n361" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit;" lang="sql">SELECT *
FROM mytable
ORDER BY col1 DESC, col2 ASC</pre> WHERE 過濾行囱桨,HAVING 過濾分組仓犬,行過濾應當先于分組過濾
-
函數(shù)
AVG COUNT MAX MIN SUM
-
COUNT(*)\COUNT(1)\COUNT(col)
COUNT(*)、COUNT(1) 返回組中的項數(shù)舍肠,包括 NULL 值和重復項
COUNT(col)為了去除col列中包含的NULL行搀继,必須讀取該col的每一行的值,然后確認下是否為NULL翠语,然后再進行計數(shù)
COUNT(1)跟COUNT(主鍵)一樣叽躯,只掃描主鍵;COUNT(*)跟COUNT(非主鍵)一樣肌括,掃描整個表
-
連接
INNER JOIN:只連接匹配的行
LEFT JOIN:返回左表的全部行和右表滿足ON條件的行点骑,如果左表的行在右表中沒有匹配,那么這一行右表中對應數(shù)據(jù)用NULL代替
RIGHT JOIN:返回右表的全部行和左表滿足ON條件的行谍夭,如果右表的行在左表中沒有匹配黑滴,那么這一行左表中對應數(shù)據(jù)用NULL代替
FULL OUTER JOIN:從左表 和右表 那里返回所有的行。如果其中一個表的數(shù)據(jù)行在另一個表中沒有匹配的行紧索,那么對面的數(shù)據(jù)用NULL代替
-
組合
UNION默認會去除相同行
如果需要保留相同行袁辈,使用 UNION ALL
-
sql語句的分析
分析慢查詢?nèi)罩荆河涗浟嗽贛ySQL中響應時間超過閥值long_query_time的SQL語句,通過日志去找出IO大的SQL以及發(fā)現(xiàn)未命中索引的SQL
使用 Explain 進行分析:通過explain命令可以得到表的讀取順序珠漂、數(shù)據(jù)讀取操作的操作類型晚缩、哪些索引可以使用、哪些索引被實際使用媳危、表之間的引用以及被掃描的行數(shù)等問題
Redis
數(shù)據(jù)類型
鍵的類型只能為字符串橡羞,值支持五種數(shù)據(jù)類型
-
字符串
可以為字符串、整數(shù)或者浮點數(shù)
應用場景:計數(shù)器(統(tǒng)計在線人數(shù)等)
底層實現(xiàn):SDS
-
列表
存儲多個字符串
擁有例如:lpush lpop rpush rpop等等操作命令
應用場景:消息隊列
底層實現(xiàn):雙向鏈表济舆,每個節(jié)點都是一個壓縮列表(包含多個entry)
-
散列表
-
底層實現(xiàn)
當存儲的數(shù)據(jù)未超過配置的閥值時卿泽,用壓縮列表(包含多個entry,每個entry都是kv對)
當存儲的數(shù)據(jù)超過配置的閥值時滋觉,用字典
應用場景:存某個對象的基本屬性信息
-
-
集合
無重復值签夭、無序
存整數(shù)時,用intset
存其他時椎侠,用字典(只有鍵第租,但沒有與鍵相關聯(lián)的值)
應用場景:去重(用戶名不能重復等)、求交集
-
有序集合
有的用壓縮列表我纪,member和score順序存放并按score的順序排列
有的用跳表+字典慎宾,跳表用來保障有序性和訪問查找性能丐吓,dict用來存儲元素信息
應用場景:范圍查找,排行榜功能趟据,topN功能
底層數(shù)據(jù)結構
-
SDS
int free; // buf數(shù)組中未使用的字節(jié)數(shù)
int len; // buf數(shù)組中已使用的字節(jié)數(shù)
char buf[]; // 字符串以'\0'結尾
append字符時券犁,若空間不夠會對SDS進行擴容:若完成后len<1MB,則free=len汹碱;若完成后len>=1MB粘衬,則free=1MB
trim字符時,并不會釋放buf的空間咳促,而是記錄在free中
-
字典
dictht 是一個散列表結構稚新,使用拉鏈法解決哈希沖突
字典包含兩個哈希表 dictht,這是為了方便進行 rehash 操作跪腹。在擴容時褂删,將其中一個 dictht 上的鍵值對 rehash 到另一個 dictht 上面,完成之后釋放空間并交換兩個 dictht 的角色
-
rehash 操作不是一次性完成冲茸,而是采用漸進方式笤妙,這是為了避免一次性執(zhí)行過多的 rehash 操作給服務器帶來過大的負擔:
例如在一次 rehash 中,要把 dict[0] rehash 到 dict[1]噪裕,這一次會把 dict[0] 上 table[rehashidx] 的鍵值對 rehash 到 dict[1] 上蹲盘,dict[0] 的 table[rehashidx] 指向 null,并令 rehashidx++
在 rehash 期間膳音,每次對字典執(zhí)行添加召衔、刪除、查找或者更新操作時祭陷,都會執(zhí)行一次漸進式 rehash
采用漸進式 rehash 會導致字典中的數(shù)據(jù)分散在兩個 dictht 上苍凛,因此對字典的查找操作也需要到對應的 dictht 去執(zhí)行
-
跳表
-
以下是查找22的過程
-
-
鏈表
- 雙向鏈表:prev next
-
整數(shù)集合 intset
int encoding; // 編碼方式
int length; // 集合包含的元素數(shù)量
int contents[]; // 保存元素的數(shù)組,從小到大排兵志,沒有重復項
-
壓縮列表
由表頭和N個entry節(jié)點和壓縮列表尾部標識符zlend組成的一個連續(xù)的內(nèi)存塊醇蝴。然后通過一系列的編碼規(guī)則,提高內(nèi)存的利用率想罕,主要用于存儲整數(shù)和比較短的字符串
在插入和刪除元素的時候悠栓,都需要對內(nèi)存進行一次擴展或縮減,還要進行部分數(shù)據(jù)的移動操作
鍵的過期時間
Redis 可以為每個鍵設置過期時間按价,當鍵過期時惭适,會自動刪除該鍵。
對于散列表這種容器楼镐,只能為整個鍵設置過期時間(整個散列表)癞志,而不能為鍵里面的單個元素設置過期時間
數(shù)據(jù)淘汰策略
可以設置內(nèi)存最大使用量,當內(nèi)存使用量超出時框产,會施行數(shù)據(jù)淘汰策略
volatile-lru 從已設置過期時間的數(shù)據(jù)集中挑選最近最少使用的數(shù)據(jù)淘汰 ? volatile-ttl 從已設置過期時間的數(shù)據(jù)集中挑選將要過期的數(shù)據(jù)淘汰 ? volatile-random 從已設置過期時間的數(shù)據(jù)集中任意選擇數(shù)據(jù)淘汰 ? allkeys-lru 從所有數(shù)據(jù)集中挑選最近最少使用的數(shù)據(jù)淘汰 ? allkeys-random 從所有數(shù)據(jù)集中任意選擇數(shù)據(jù)進行淘汰 ? noeviction 禁止驅(qū)逐數(shù)據(jù)
持久化方法
-
RDB持久化
將某個時間點的所有數(shù)據(jù)都存放到硬盤上
如果系統(tǒng)發(fā)生故障凄杯,將會丟失最后一次創(chuàng)建快照之后的數(shù)據(jù)
如果數(shù)據(jù)量很大错洁,保存快照的時間會很長
-
AOF持久化
把寫操作指令,持續(xù)的寫到一個類似日志的文件里戒突,即 AOF 文件(Append Only File)屯碴,通過日志恢復
隨著服務器寫請求的增多,AOF 文件會越來越大妖谴。Redis 提供了一種將 AOF 重寫的特性窿锉,能夠去除 AOF 文件中的冗余寫命令
-
使用 AOF 持久化需要設置同步選項酌摇,從而確保寫命令同步到磁盤文件上的時機膝舅。這是因為對文件進行寫入并不會馬上將內(nèi)容同步到磁盤上,而是先存儲到緩沖區(qū)窑多,然后由操作系統(tǒng)決定什么時候同步到磁盤仍稀。有以下同步選項:
always 每個寫命令都同步
everysec 每秒同步一次
no 讓操作系統(tǒng)來決定何時同步
主從復制
一個從服務器只能有一個主服務器
-
過程
主服務器創(chuàng)建快照文件,發(fā)送給從服務器埂息,并在發(fā)送期間使用緩沖區(qū)記錄執(zhí)行的寫命令技潘;快照文件發(fā)送完畢之后,開始向從服務器發(fā)送存儲在緩沖區(qū)中的寫命令
從服務器丟棄所有舊數(shù)據(jù)千康,載入主服務器發(fā)來的快照文件享幽,之后從服務器開始接受主服務器發(fā)來的寫命令
主服務器每執(zhí)行一次寫命令,就向從服務器發(fā)送相同的寫命令
Sentinel(哨兵)可以監(jiān)聽集群中的服務器拾弃,并在主服務器進入下線狀態(tài)時值桩,自動從從服務器中選舉出新的主服務器
-
好處
讀寫分離:主服務器負責寫,從服務器負責讀
數(shù)據(jù)實時備份豪椿,當系統(tǒng)中某個節(jié)點發(fā)生故障時奔坟,可以方便的故障切換
降低單個服務器磁盤I/O訪問的頻率,提高單個機器的I/O性能
redis vs memcached
都是非關系型內(nèi)存鍵值數(shù)據(jù)庫搭盾,但有以下區(qū)別
Memcached 僅支持字符串類型咳秉,而 Redis 支持五種不同的數(shù)據(jù)類型
Redis 支持兩種持久化策略:RDB 快照和 AOF 日志,而 Memcached 不支持持久化
Memcached 不支持分布式鸯隅,只能通過在客戶端使用一致性哈希來實現(xiàn)分布式存儲澜建,這種方式在存儲和查詢時都需要先在客戶端計算一次數(shù)據(jù)所在的節(jié)點;Redis Cluster 實現(xiàn)了分布式的支持(主從復制)
Memcached是多線程蝌以,非阻塞IO復用的網(wǎng)絡模型霎奢;Redis使用單線程的多路 IO 復用模型
對一致性的保證:redis單線程模型,保證數(shù)據(jù)按順序提交饼灿,利用隊列技術將并發(fā)訪問變?yōu)榇性L問幕侠;memcached使用cas保持一致性
redis vs mongodb
redis數(shù)據(jù)全在內(nèi)存,定期寫入磁盤碍彭,當內(nèi)存不夠時晤硕,采用LRU算法刪除數(shù)據(jù)悼潭;mongodb數(shù)據(jù)存在內(nèi)存,由linux的mmap實現(xiàn)舞箍,當內(nèi)存不夠時舰褪,只將熱點數(shù)據(jù)放入內(nèi)存,其他數(shù)據(jù)存在磁盤
redis支持五種數(shù)據(jù)類型疏橄;mongodb存放的是文檔
redis是單線程的占拍,為何如此高效
完全基于內(nèi)存,絕大部分請求是純粹的內(nèi)存操作捎迫,非郴尉疲快速
數(shù)據(jù)以kv對形式存放,查找效率高
雖然redis文件事件處理器以單線程方式運行窄绒,但使用了I/O多路復用程序來監(jiān)聽多個套接字贝次,I/O多路復用程序通過隊列向文件事件分派器傳送socket
數(shù)據(jù)結構簡單,對數(shù)據(jù)操作也簡單
采用單線程彰导,避免了不必要的上下文切換和競爭條件蛔翅,也不存在多進程或者多線程導致的切換而消耗 CPU,不用去考慮各種鎖的問題位谋,不存在加鎖釋放鎖操作山析,沒有因為可能出現(xiàn)死鎖而導致的性能消耗
Redis直接自己構建了VM 機制 ,因為一般的系統(tǒng)調(diào)用系統(tǒng)函數(shù)的話掏父,會浪費一定的時間去移動和請求
redis為什么不用多線程
多線程處理可能涉及到鎖
多線程處理會涉及到線程切換而消耗CPU
單線程處理的缺點
耗時的命令會導致并發(fā)的下降笋轨,不只是讀并發(fā),寫并發(fā)也會下降
無法發(fā)揮多核CPU性能损同,不過可以通過在單機開多個Redis實例來完善
redis有線程安全問題嗎
Redis采用了線程封閉的方式翩腐,把任務封閉在一個線程,自然避免了線程安全問題膏燃,不過對于需要依賴多個redis操作(即:多個Redis操作命令)的復合操作來說茂卦,依然需要鎖,而且有可能是分布式鎖
redis是怎么實現(xiàn)串行化的
redis利用隊列技術將并發(fā)訪問變?yōu)榇性L問组哩,消除了傳統(tǒng)數(shù)據(jù)庫串行控制的開銷
數(shù)據(jù)庫范式
第一范式 1NF
屬性不應該是可分的
舉例:如果將“電話”作為一個屬性(一列)等龙,是不符合1NF的,因為電話這個屬性可以分解為家庭電話和移動電話等伶贰;如果將“移動電話”作為一個屬性蛛砰,就符合1NF
第二范式 2NF
每個非主屬性完全依賴于主屬性集
舉例:(學號,課程名)這個主屬性集可以唯一決定成績黍衙,但是對于學生姓名這個屬性泥畅,(學號,課程名)這個屬性集就是冗余的琅翻,所以學生姓名不完全依賴于(學號位仁,課程名)這一屬性集
第三范式 3NF
在 2NF 的基礎上柑贞,非主屬性不傳遞依賴于主屬性
舉例:比如一個表中,主屬性有(學號)聂抢,非主屬性有(姓名钧嘶,院系,院長名)琳疏,可以看到院長名這個非主屬性依賴于院系有决,傳遞依賴于學號
不符合范式會有什么問題
冗余數(shù)據(jù):某些同樣的數(shù)據(jù)多次出現(xiàn)(如學生姓名)
修改異常:修改了一個記錄中的信息,另一個記錄中相同的信息卻沒有修改
刪除異常:刪除一個信息空盼,那么也會丟失其它信息(刪除一個課程书幕,丟失了一個學生的信息)
插入異常:無法插入(插入一個還沒有課程信息的學生)
存儲過程
定義
事先經(jīng)過編譯并存儲在數(shù)據(jù)庫中的一段SQL語句的集合。想要實現(xiàn)相應的功能時我注,只需要調(diào)用這個存儲過程就行了(類似于函數(shù)按咒,輸入具有輸出參數(shù))
優(yōu)點
預先編譯迟隅,而不需要每次運行時編譯但骨,提高了數(shù)據(jù)庫執(zhí)行效率;
封裝了一系列操作智袭,對于一些數(shù)據(jù)交互比較多的操作奔缠,相比于單獨執(zhí)行SQL語句,可以減少網(wǎng)絡通信量吼野;
具有可復用性校哎,減少了數(shù)據(jù)庫開發(fā)的工作量
drop vs delete vs truncate
delete
刪除表的全部或者部分數(shù)據(jù)
執(zhí)行delete之后,用戶需要提交之后才會執(zhí)行
DELETE之后表結構還在
刪除很慢瞳步,一行一行地刪
因為會記錄日志闷哆,可以利用日志還原數(shù)據(jù)
truncate
刪除表中的所有數(shù)據(jù)
不能回滾
操作比DELETE快很多(直接把表drop掉,再創(chuàng)建一個新表单起,刪除的數(shù)據(jù)不能找回)
drop
從數(shù)據(jù)庫中刪除表
所有的數(shù)據(jù)行抱怔,索引和約束都會被刪除
不能回滾
連接池
定義
程序啟動時建立足夠的數(shù)據(jù)庫連接,并將這些連接組成一個連接池嘀倒,由程序動態(tài)地對連接池中的連接進行申請,使用,釋放奸远。系統(tǒng)在整個程序運行結束的時候再把數(shù)據(jù)庫連接關閉
好處
用空間換時間的思想制肮,系統(tǒng)啟動預先創(chuàng)建多個數(shù)據(jù)庫連接對象雖然會占用一定的內(nèi)存空間,但是可以省去后面每次SQL查詢時創(chuàng)建連接和關閉連接消耗的時間
如果無連接池有很大的訪問量碳胳,數(shù)據(jù)庫服務器就需要為每次連接創(chuàng)建一次數(shù)據(jù)庫連接勇蝙,極大的浪費數(shù)據(jù)庫資源,并且極易造成數(shù)據(jù)庫服務器內(nèi)存溢出挨约;而連接池的最大數(shù)據(jù)庫連接數(shù)量限定了這個連接池能占有的最大連接數(shù)味混,當應用程序向連接池請求連接數(shù)量超過最大連接數(shù)量時藕帜,這些請求將被加入到等待隊列中
如何提高mysql的讀寫性能
讀寫分離:讓主數(shù)據(jù)庫處理寫,而從數(shù)據(jù)庫處理讀
選擇合適的存儲引擎:主庫配置innodb惜傲;從庫可配置myisam引擎洽故,提升查詢性能以及節(jié)約系統(tǒng)開銷
把數(shù)據(jù)存在內(nèi)存中:設置足夠大的 innodb_buffer_pool_size
數(shù)據(jù)預熱:數(shù)據(jù)庫剛剛啟動,需要進行數(shù)據(jù)預熱盗誊,將磁盤上的所有數(shù)據(jù)緩存到內(nèi)存中
減少磁盤寫入操作:使用足夠大的寫入緩存 innodb_log_file_size时甚;innodb_flush_log_at_trx_commit設置為每秒寫入磁盤而不是每次修改都寫入磁盤
使用合適的索引
分析查詢?nèi)罩竞吐樵內(nèi)罩?/p>
分庫 vs 分表
分庫
表的垂直拆分(分庫):把含有多個列的表拆分成多個表:
把不常用的字段單獨放在同一個表中
把大字段獨立放入一個表中
分表
表的水平拆分(分表):解決數(shù)據(jù)表中數(shù)據(jù)過多的問題
降低在查詢時需要讀的數(shù)據(jù)和索引的頁數(shù),同時也降低了索引的層數(shù)
把數(shù)據(jù)存放到多個數(shù)據(jù)庫中哈踱,提高系統(tǒng)的總體可用性(分庫荒适,雞蛋不能放在同一個籃子里)