1. 程式人生 > 其它 >秋招面試知識點----資料庫篇

秋招面試知識點----資料庫篇

技術標籤:索引資料庫分散式redismysql

B樹

B樹相對於平衡二叉樹的不同是,每個節點包含的關鍵字增多了,特別是在B樹應用到資料庫中的時候,資料庫充分利用了磁碟塊的原理就是區域性i性的思想,裝入部分就可以執行,
在B樹的基礎上每個節點儲存的關鍵字數更多,樹的層級更少所以查詢資料更快,所有指關鍵字指標都存在葉子節點,所以每次查詢的次數都相同所以查詢速度更穩定;

mysql B+

1)B+跟B樹不同B+樹的非葉子節點不儲存關鍵字記錄的指標,只進行資料索引,這樣使得B+樹每個非葉子節點所能儲存的關鍵字大大增加;

(2)B+樹葉子節點儲存了父節點的所有關鍵字記錄的指標,所有資料地址必須要到葉子節點

才能獲取到。所以每次資料查詢的次數都一樣,比較穩定;

(3)B+樹葉子節點的關鍵字從小到大有序排列,左邊結尾資料都會儲存右邊節點開始資料的指標。

(4)非葉子節點的子節點數=關鍵字數(雖然他們資料排列結構不一樣,但其原理還是一樣的Mysql 的B+樹是用第一種方式實現);

為啥用B+B+

1.檔案很大,不可能全部儲存在記憶體中,故要儲存到磁碟上,索引的結構組織要儘量減少查詢過程中磁碟I/O的存取次數(為什麼使用B-/+Tree,還跟磁碟存取原理有關。)

3.區域性性原理與磁碟預讀,預讀的長度一般為頁(page)的整倍數,(在許多作業系統中,頁得大小通常為4k)

****4.**資料庫系統巧妙利用了磁碟預讀原理,將一個節點的大小設為等於一個頁,這樣*每個節點只需要一次I/O*就可以完全載入,(由於節點中有兩個陣列,所以地址連續)。而紅黑樹這種結構,  而紅黑樹這種結構,h

明顯要深的多。由於邏輯上很近的節點(父子)物理上可能很遠,無法利用區域性性,所以紅黑樹的I/O漸進複雜度也為O(h),效率明顯比B-Tree差很多。

1.Hash索引僅僅能滿足“=”,“IN”,不能支援範圍查詢

2.對於排序操作Hash索引也滿足不了

3.Hash索引不能避免表掃描

4.當有大量資料的Hash值相等的時候Hash索引的效能大打折扣***h*明顯要深的多。由於邏輯上很近的節點(父子)物理上可能很遠,無法利用區域性性

索引

普通索引,唯一索引,主鍵索引

第一種方式 :

  CREATE INDEX account_Index ON `award`(`account`);

第二種方式:

ALTER TABLE award ADD INDEX account_Index(`account`)

全文索引

文字欄位上(text)如果建立的是普通索引,那麼只有對文字的欄位內容前面的字元進行索引,其字元大小根據索引建立索引時申明的大小來規定.

如果文字中出現多個一樣的字元,而且需要查詢的話,那麼其條件只能是 where column lick ‘%xxxx%’ 這樣做會讓索引失效

.這個時候全文索引就祈禱了作用了

ALTER TABLE tablename ADD FULLTEXT(column1, column2)

有了全文索引,就可以用SELECT查詢命令去檢索那些包含著一個或多個給定單詞的資料記錄了。

在經常需要搜尋的列上**

  • 主鍵列上可以確保列的唯一性
  • 在表與表的而連線條件上加上索引,可以加快連線查詢的速度
  • 在經常需要排序(order by),分組(group by)和的 distinct 列上加索引可以加快排序查詢

哪些情況不適合建立索引?
1.查詢中很少使用到的列
2.很少資料的列,男女重複性太高
3.定義為 text 和 image 和 bit 資料型別的列
4.表的修改大大多於查詢

哪些情況會造成索引失效?

  • 如果條件中有 or,即使其中有條件帶索引也不會使用(這也是為什麼要少用 or 的原因)

  • 索引欄位的值不能有 null 值

  • like 查詢以%開頭,字串型別的欄位索引按首字母開頭構成一個tree,

  • 在索引列上做計算,c+=100不行

  • 函式

  • 型別轉換等)

  • 負面操作,比如is not, not in, !=, <>, is not null,都用不上索引

慢sql

一個 SQL 執行的很慢,我們要分兩種情況討論:

1、偶爾很慢**,則有如下原因

(1)、資料庫在重新整理髒頁,例如 redo log 寫滿了需要同步到磁碟。

(2)、執行的時候,遇到鎖,如表鎖、行鎖。

2、一直執行的很慢,則有如下原因。

(1)、沒有用上索引:例如該欄位沒有索引;由於對欄位進行運算、函式操作導致無法用索引。

(2)、資料庫選錯了索引。

explain

# 建索引 create index idx on emp(age, deptId, name);

  • **id:**就是數值越大越先執行,
  • select_type:
    • simple,表示此查詢不包含union查詢或者子查詢,簡單粗暴
    • primary,表示此查詢是最外層的查詢,就是父級
    • union,表示此查詢是union的第二或者隨後的查詢
  • table:資料是哪張表的
  • type:
    • system:表僅有一行(=系統表)。這是const聯接型別的一個特例,想屁吃。
    • const:表最多有一個匹配行,它將在查詢開始時被讀取。因為僅有一行,在這行的列值可被優化器剩餘部分認為是常數。const表很快,因為它們只讀取一次!例如,name=“張三”
    • eq_ref:對於每個來自於前面的表的行組合,從該表中讀取一行。這可能是最好的聯接型別,除了const型別,做到這,用到了索引,還只有一條資料。
    • ref:對於每個來自於前面的表的行組合,所有有匹配索引值的行將從這張表中讀取,就是也用了索引,就是結果的元組多個
    • **ALL:**對於每個來自於先前的表的行組合,進行完整的表掃描
  • possible_keys:理論使用
  • **key:**實際使用
  • **keylen:**就是用了多少索引
  • **rows:**就是搜多少找到的
  • extra:
    • Using filesort :說明mysql會對資料使用一個外部的索引排序,而不是按照表內的索引順序進行讀取。MySQL中無法利用索引完成的排序操作稱為“檔案排序”,這一般是需要優化的。
    • Using temporary :使了用臨時表儲存中間結果,MySQL在對查詢結果排序時使用臨時表。常見於排序 order by 和分組查詢 group by這一般是需要優化的。
    • USING index:表示相應的select操作中使用了覆蓋索引(Covering Index),避免訪問了表的資料行,效率不錯。如果同時出現using where,表明索引被用來執行索引鍵值的查詢;如果沒有同時出現using where,表明索引只是用來讀取資料而非利用索引執行查詢,一般就是沒有where

sql執行

聯結器: 身份認證和許可權相關(登入 MySQL 的時候)。

查詢快取: 執行查詢語句的時候,會先查詢快取(MySQL 8.0 版本後移除,因為這個功能不太實用)。

分析器: 沒有命中快取的話,SQL 語句就會經過分析器,分析器說白了就是要先看你的 SQL 語句要幹嘛,再檢查你的 SQL 語句語法是否正確。

優化器: 按照 MySQL 認為最優的方案去執行。

執行器: 執行語句,然後從儲存引擎返回資料。

SQL 等執行過程分為兩類,

一類對於查詢等過程如下:許可權校驗—》查詢快取—》分析器—》優化器—》許可權校驗—》執行器—》引擎

對於更新等語句執行流程如下:分析器----》許可權校驗----》執行器—》引擎—redo log prepare—》binlog—》redo log commit

MVCC多版本併發控制

就是多版本併發控制,解決資料庫併發的問題,事務的實現主要體現在讀已提交,可重複讀,他有一個版本連的東西,就是表裡面有兩個隱藏的列,一個是事務id,一個是上一個版本的位置,方便回退;

​ 就是進行修改時,老版本寫到undo日誌,undo日誌中有版本鏈
在這裡插入圖片描述

readview:

讀已提交和重複讀就是readview策略不同,就是一個列表有著事務id,是那些開始的食物但是還沒提交的

例子:a=100,沒提交,我去查是查不到的,readview裡面有100;兩種的策略都查不到,但是現在提交了,有新開一個事務a=200;

讀已提交就是會更新readview,表裡的100沒了可以讀到a=100

可重複讀就是不更新readview,所以獨到的是a=100之前的數;

Redis五種資料型別

Redis對於過期鍵有三種清除策略:

  • 被動刪除:當讀/寫一個已經過期的key時,會觸發惰性刪除策略,直接刪除掉這個過期key
  • 主動刪除:由於惰性刪除策略無法保證冷資料被及時刪掉,所以Redis會定期主動淘汰一批已過期的key
  • 當前已用記憶體超過maxmemory限定時,觸發主動清理策略

redis持久化

rdb

Redis會單獨fork一個子程序來進行持久化,會先將資料寫入到一個臨時檔案中,待持久化過程 都結束了,再用這個臨時檔案替換上次持久化好的檔案。整個過程中,主程序是不進行任何IO操作的。 這就很高效能。如果需要進行大規模資料的恢復,且對於資料恢復的完整性無所謂,那 RDB方式要比AOF方式更加的高效。RDB的缺點是後一次持久化後的資料可能丟失。我們預設的就是 RDB,一般情況下不需要修改這個配置!會有sava 9 100,就是fork程序消耗資源,60秒以儲存,可能會丟東西

aof

將我們的所有命令都記錄下來,history,恢復的時候就把這個檔案全部在執行一遍!

appendfsync always 每次修改都會 sync。消耗效能

appendfsync everysec 每秒執行一次 sync,可能會丟失這1s的資料!

appendfsync no # 不執行 sync,這個時候作業系統自己同步資料,速度快

RDB與AOF的選擇

  • RDB與AOF的選擇世界上是在做一種權衡,每種都有利弊

  • 如不能承受數分鐘以內的資料丟失,對業務資料非常敏感,選用AOF

  • 如能承受數分鐘以內的資料丟失,且追求大資料集的恢復速度,先用RDB

  • 災難恢復選用RDB

  • 雙保險策略:同時開啟RDB與AOF,重啟後,Redis優先選擇使用AOF來恢復速度,降低丟失資料的量分散式鎖

    分散式鎖:當多個程序不在同一個系統中,用分散式鎖控制多個程序對資源的訪問。

    eval命令執行Lua程式碼的時候,Lua程式碼將被當成一個命令去執行,並且直到eval命令執行完成,Redis才會執行其他命令。

分散式鎖

加鎖

最簡單的方法是使用 setnx 命令。key 是鎖的唯一標識,按業務來決定命名。比如想要給一種商品的秒殺活動加鎖,可以給 key 命名為 “lock_sale_商品ID” 。而 value 設定成什麼呢?我們可以姑且設定成 1。加鎖的虛擬碼如下:

setnx(lock_sale_商品ID,1)

當一個執行緒執行 setnx 返回 1,說明 key 原本不存在,該執行緒成功得到了鎖;當一個執行緒執行 setnx 返回 0,說明 key 已經存在,該執行緒搶鎖失敗。

解鎖

有加鎖就得有解鎖。當得到鎖的執行緒執行完任務,需要釋放鎖,以便其他執行緒可以進入。釋放鎖的最簡單方式是執行 del 指令,虛擬碼如下:

del(lock_sale_商品ID)

釋放鎖之後,其他執行緒就可以繼續執行 setnx 命令來獲得鎖。

鎖超時

鎖超時是什麼意思呢?如果一個得到鎖的執行緒在執行任務的過程中掛掉,來不及顯式地釋放鎖,這塊資源將會永遠被鎖住(死鎖),別的執行緒再也別想進來。所以,setnxkey 必須設定一個超時時間,以保證即使沒有被顯式釋放,這把鎖也要在一定時間後自動釋放。setnx 不支援超時引數,所以需要額外的指令,虛擬碼如下:

expire(lock_sale_商品ID, 30)

三種弊端

1. setnxexpire` 的非原子性

設想一個極端場景,當某執行緒執行 setnx,成功得到了鎖:

setnx 剛執行成功,還未來得及執行 expire 指令,節點 1 掛掉了。

這樣一來,這把鎖就沒有設定過期時間,變成死鎖,別的執行緒再也無法獲得鎖了。

怎麼解決呢?setnx 指令本身是不支援傳入超時時間的,set 指令增加了可選引數,虛擬碼如下:

set(lock_sale_商品ID,1,30,NX)

這樣就可以取代 setnx 指令。

2. del` 導致誤刪

又是一個極端場景,假如某執行緒成功得到了鎖,並且設定的超時時間是 30 秒。

如果某些原因導致執行緒 A 執行的很慢很慢過了 30 秒都沒執行完,這時候鎖過期自動釋放,執行緒 B 得到了鎖。

隨後,執行緒 A 執行完了任務,執行緒 A 接著執行 del 指令來釋放鎖。但這時候執行緒 B 還沒執行完,執行緒A實際上 刪除的是執行緒 B 加的鎖

**怎麼避免這種情況呢?**可以在 del 釋放鎖之前做一個判斷,驗證當前的鎖是不是自己加的鎖。至於具體的實現,可以在加鎖的時候把當前的執行緒 ID 當做 value,並在刪除之前驗證 key 對應的 value 是不是自己執行緒的 ID

加鎖:

String threadId = Thread.currentThread().getId()
set(key,threadId ,30,NX)

解鎖:

if(threadId .equals(redisClient.get(key)){
    del(key)
}

但是,這樣做又隱含了一個新的問題,判斷和釋放鎖是兩個獨立操作,不是原子性。

3. 出現併發的可能性

還是剛才第二點所描述的場景,雖然我們避免了執行緒 A 誤刪掉 key 的情況,但是同一時間有 A,B 兩個執行緒在訪問程式碼塊,仍然是不完美的。怎麼辦呢?我們可以讓獲得鎖的執行緒開啟一個守護執行緒,用來給快要過期的鎖“續航”。

當過去了 29 秒,執行緒 A 還沒執行完,這時候守護執行緒會執行 expire 指令,為這把鎖“續命”20 秒。守護執行緒從第 29 秒開始執行,每 20 秒執行一次。

當執行緒 A 執行完任務,會顯式關掉守護執行緒。

另一種情況,如果節點 1 忽然斷電,由於執行緒 A 和守護執行緒在同一個程序,守護執行緒也會停下。這把鎖到了超時的時候,沒人給它續命,也就自動釋放了。

redis主庫和從庫不一致

主從同步有時延,這個時延期間讀從庫,可能讀到不一致的資料。

例如a在讀取,b改了,同步的時候,a的從機收不到;

(1)業務可以接受,系統不優化

(2)強制讀主,高可用主庫,用快取提高讀效能

(3)在cache裡記錄哪些記錄發生過寫請求,來路由讀主還是讀從

雪崩解決

redis高可用 這個思想的含義是,既然redis有可能掛掉,那我多增設幾臺redis,這樣一臺掛掉之後其他的還可以繼續 工作,其實就是搭建的叢集。(異地多活!)
限流降級
這個解決方案的思想是,在快取失效後,通過加鎖或者佇列來控制讀資料庫寫快取的執行緒數量。比如對 某個key只允許一個執行緒查詢資料和寫快取,其他執行緒等待。
資料預熱
資料加熱的含義就是在正式部署之前,我先把可能的資料先預先訪問一遍,這樣部分可能大量訪問的數 據就會載入到快取中。在即將發生大併發訪問前手動觸發載入快取不同的key,設定不同的過期時間,讓 快取失效的時間點儘量均勻