資料庫基礎概念
什麼是資料庫
資料庫(Database) 是儲存有組織的資料的容器(通常是一個檔案或一組檔案),是通過 資料庫管理系統(DataBase- Management System,DBMS) 建立和操縱的容器。DBMS 的主要目標是提供一種可以方便、高效地存取資料庫資訊的途徑。
我們常說 XX 資料庫,其實實質上是 XX 資料庫管理系統。目前,較為流行的資料庫管理系統有 MySQL、SQL Server、Oracle 等。
資料庫有兩種型別,分別是 關係型資料庫 和 非關係型資料庫。
注:SQL 是 結構化查詢語言(Structured Query Language) 的縮寫,是一種資料庫查詢語言,用於存取資料、查詢、更新和管理關聯式資料庫系統。與其他語言(如英語以及 Java 等程式設計語言)不一樣,SQL 由少量的描述性很強的詞構成,簡單易學。
為什麼要使用資料庫
總而言之,面對龐大的網路資料量,使用資料庫可以高效且條理分明地儲存資料,它使人們能夠更加迅速和方便地管理資料。
SQL與MySQL有什麼區別
SQL 和 MySQL 是 DBMS 中最令人困惑的兩個術語,二者之間存在本質上的區別。
- SQL 是一種 結構化查詢語言,用於在資料庫上執行各種操作,但 MySQL 是一個 關聯式資料庫管理系統(RDBMS),使用 SQL 執行所有資料庫操作。
- SQL 用於訪問,更新和運算元據庫中的資料,使用者使用時需要學習該語言,然後編寫查詢,而 MySQL 是一個軟體,會為使用者提供一個介面,只需單擊一些按鈕即可用於執行各種資料庫操作。
- 由於 MySQL 是一個軟體,所以它會定期獲得各種更新,但在 SQL 中,命令總是相同的。
資料庫三大正規化是什麼
資料庫正規化是設計資料庫時,需要遵循的一些規範。各種正規化是條件遞增的聯絡,越高的正規化資料庫冗餘越小。常用的資料庫三大正規化為:
- 第一正規化(1NF):每個列都不可以再拆分,強調的是列的原子性。第一正規化要求資料庫中的表都是二維表。
- 第二正規化(2NF):在第一正規化的基礎上,一個表必須有一個主鍵,非主鍵列 完全依賴 於主鍵,而不能是依賴於主鍵的一部分。
- 第三正規化(3NF):在第二正規化的基礎上,非主鍵列只依賴(直接依賴)於主鍵,不依賴於其他非主鍵。
資料庫連線洩露的含義
資料庫連線洩露指的是如果在某次使用或者某段程式中沒有正確地關閉 Connection、Statement 和 ResultSet 資源,那麼每次執行都會留下一些沒有關閉的連線,這些連線失去了引用而不能得到重新使用,因此就造成了資料庫連線的洩漏。資料庫連線的資源是寶貴而且是有限的,如果在某段使用頻率很高的程式碼中出現這種洩漏,那麼資料庫連線資源將被耗盡,影響系統的正常運轉。
什麼是觸發器
觸發器(trigger)是與表相關的資料庫物件,是使用者定義在關係表上的一類由事件驅動的特殊的儲存過程,在滿足定義條件時觸發,並執行觸發器中定義的 語句集合。觸發器的這種特性可以協助應用在資料庫端確保 資料庫的完整性。
使用場景
- 可以通過資料庫中的相關表實現級聯更改;
- 實時監控某張表中的某個欄位的更改,並需要做出相應的處理。
索引
索引的優缺點
索引的優點
- 通過建立 唯一性索引,可以保證資料庫表中每一行資料的唯一性;
- 可以加快資料的 檢索速度,這也是建立索引的主要原因;
- 可以加速表和表之間的連線,特別是在實現 資料的參考完整性方面特別有意義;
- 通過使用索引,可以在查詢的過程中,使用 優化隱藏器,提高系統效能。
索引的缺點
- 時間上,建立和維護索引都要耗費時間,這種時間隨著資料量的增加而增加,具體地,當對錶中的資料進行增加、刪除和修改的時候,索引也要動態的維護,這樣就降低了資料的維護速度;
- 空間上,索引需要佔 物理空間,除了資料表佔資料空間之外,每一個索引還要佔一定的物理空間,如果要建立聚簇索引,那麼需要的空間就會更大。
索引的資料結構
資料庫索引根據結構分類,主要有 B 樹索引、Hash 索引 和 點陣圖索引 三種。
B 樹索引
B 樹索引,又稱 平衡樹索引,是 MySQL 資料庫中使用最頻繁的索引型別,MySQL、Oracle 和 SQL Server 資料庫預設的都是 B 樹索引(實際是用 B+ 樹實現的,因為在檢視錶索引時,MySQL 一律列印 BTREE,所以簡稱為 B 樹索引)。
B 樹索引以 樹結構 組織,它有一個或者多個分支結點,分支結點又指向單級的葉結點。其中,分支結點用於遍歷樹,葉結點則儲存真正的值和位置資訊。
B+ 樹是在 B 樹基礎上的一種優化,使其更適合實現外儲存索引結構。
一棵 m 階 B-Tree 的特性如下:
每個結點最多 m 個子結點;
除了根結點和葉子結點外,每個結點最少有 m/2(向上取整)個子結點;
所有的葉子結點都位於同一層;
每個結點都包含 k 個元素(關鍵字),這裡 m/2≤k<m,這裡 m/2 向下取整;
每個節點中的元素(關鍵字)從小到大排列;
每個元素子左結點的值,都小於或等於該元素,右結點的值都大於或等於該元素。
資料庫以 B-Tree 的資料結構儲存資料的圖示如下:
B+ Tree 與 B-Tree 的結構很像,但是也有自己的特性:
所有的非葉子結點只儲存 關鍵字資訊;
所有具體資料都存在葉子結點中;
所有的葉子結點中包含了全部元素的資訊;
所有葉子節點之間都有一個鏈指標。
資料庫以 B+ Tree 的資料結構儲存資料的圖示如下:
Hash 索引
雜湊索引採用一定的 雜湊演算法(常見雜湊演算法有 直接定址法、平方取中法、摺疊法、除數取餘法、隨機數法),將資料庫欄位資料轉換成定長的 Hash 值,與這條資料的行指標一併存入 Hash 表的對應位置,如果發生 Hash 碰撞(兩個不同關鍵字的 Hash 值相同),則在對應 Hash 鍵下以 連結串列形式 儲存。
檢索時不需要類似 B+ 樹那樣從根節點到葉子節點逐級查詢,只需一次雜湊演算法即可立刻定位到相應的位置,速度非常快,平均檢索時間為 O(1)。
點陣圖索引
B 樹索引擅長於處理包含許多不同值的列,但是在處理基數較小的列時會變得很難使用。如果使用者查詢的列的基數非常的小, 即只有幾個固定值,如性別、婚姻狀況、行政區等等,要麼不使用索引,查詢時一行行掃描所有記錄,要麼考慮建立點陣圖索引。
點陣圖索引為儲存在某列中的每個值生成一個點陣圖。例如針對表中婚姻狀況這一列,生成的點陣圖索引大致如下所示:
使用B+樹的好處
- 由於 B+ 樹的內部結點只存放鍵,不存放值,因此,一次讀取,可以在同一記憶體頁中獲取更多的鍵,有利於更快地縮小查詢範圍。
- B+ 樹的葉結點由一條鏈相連,因此當需要進行一次 全資料遍歷 的時候,B+ 樹只需要使用 O(logN) 時間找到最小結點,然後通過鏈進行 O(N) 的順序遍歷即可;或者,在找 大於某個關鍵字或者小於某個關鍵字的資料 的時候,B+ 樹只需要找到該關鍵字然後沿著連結串列遍歷即可。
Hash索引和B+樹索引的區別
Hash 索引和 B+ 樹索引有以下幾點顯見的區別:
- Hash 索引進行等值查詢更快(一般情況下),但是卻無法進行範圍查詢;
- Hash 索引不支援使用索引進行排序;
- Hash 索引不支援模糊查詢以及多列索引的最左字首匹配,原理也是因為 Hash 函式的不可預測;
- Hash 索引任何時候都避免不了回表查詢資料,而 B+ 樹在符合某些條件(聚簇索引,覆蓋索引等)的時候可以只通過索引完成查詢;
- Hash 索引雖然在等值查詢上較快,但是不穩定,效能不可預測,當某個鍵值存在大量重複的時候,發生 Hash 碰撞,此時效率可能極差;而 B+ 樹的查詢效率比較穩定,對於所有的查詢都是從根結點到葉子結點,且樹的高度較低。
什麼是字首索引
有時需要索引很長的字元列,它會使索引變大並且變慢,一個策略就是索引開始的幾個字元,而不是全部值,即被稱為 字首索引,以節約空間並得到好的效能。使用字首索引的前提是 此字首的標識度高,比如密碼就適合建立字首索引,因為密碼幾乎各不相同。
字首索引需要的空間變小,但也會降低選擇性。索引選擇性(INDEX SELECTIVITY)是不重複的索引值(也叫基數)和表中所有行數(T)的比值,數值範圍為 1/T ~1。高選擇性的索引有好外,因為在查詢匹配的時候可以過濾掉更多的行,唯一索引的選擇率為 1,為最佳值。對於字首索引而言,字首越長往往會得到好的選擇性,但是短的字首會節約空間,所以實操的難度在於字首擷取長度的抉擇,可以通過除錯檢視不同字首長度的 平均匹配度,來選擇擷取長度。
什麼是最左字首匹配原則
在 MySQL 建立 聯合索引(多列索引) 時會遵守最左字首匹配原則,即 最左優先,在檢索資料時從聯合索引的最左邊開始匹配。例如有一個 3 列索引(a,b,c),則已經對(a)、(a,b)、(a,b,c)上建立了索引。所以在建立 多列索引時,要根據業務需求,where 子句中 使用最頻繁 的一列放在最左邊。
根據最左字首匹配原則,MySQL 會一直向右匹配直到遇到 範圍查詢(>、<、between、like)就停止匹配,比如採用查詢條件 where a = 1 and b = 2 and c > 3 and d = 4
時,如果建立(a,b,c,d)順序的索引,d 是用不到索引的,如果建立(a,b,d,c)的索引則都可以用到,並且 where 子句中 a、b、d 的順序可以任意調整。
如果建立的索引順序是 (a,b) ,那麼根據最左字首匹配原則,直接採用查詢條件 where b = 1 是無法利用到索引的。
新增索引的原則
索引雖好,但也不是無限制使用的,以下為新增索引時需要遵循的幾項建議性原則:
- 在 查詢中很少使用 或者參考的列不要建立索引。由於這些列很少使用到,增加索引反而會降低系統的維護速度和增大空間需求。
- 只有很少資料值的列 也不應該增加索引。由於這些列的取值很少,區分度太低,例如人事表中的性別,在查詢時,需要在表中搜尋的資料行的比例很大。增加索引,並不能明顯加快檢索速度。
- 定義為 text、image 和 bit 資料型別的列不應該增加索引。這是因為,這些列的資料量要麼相當大,要麼取值很少。
- 當 修改效能遠遠大於檢索效能 時,不應該建立索引。這時因為,二者是相互矛盾的,當增加索引時,會提高檢索效能,但是會降低修改效能。
- 定義有 外來鍵 的資料列一定要建立索引。
什麼是聚簇索引
聚簇索引,又稱 聚集索引, 首先並不是一種索引型別,而是一種資料儲存方式。具體的,聚簇索引指將 資料儲存 和 索引 放到一起,找到索引也就找到了資料。
MySQL 裡只有 INNODB 表支援聚簇索引,INNODB 表資料本身就是聚簇索引,非葉子節點按照主鍵順序存放,葉子節點存放主鍵以及對應的行記錄。所以對 INNODB 表進行全表順序掃描會非常快。
特點
- 因為索引和資料存放在一起,所以具有更高的檢索效率
- 相比於非聚簇索引,聚簇索引可以減少磁碟的 IO 次數;
- 表的物理儲存依據聚簇索引的結構,所以一個資料表只能有一個聚簇索引,但可以擁有多個非聚簇索引;
- 一般而言,會在頻繁使用、排序的欄位上建立聚簇索引。
非聚簇索引
除了聚簇索引以外的其他索引,均稱之為非聚簇索引。非聚簇索引也是 B 樹結構,與聚簇索引的儲存結構不同之處在於,非聚簇索引中不儲存真正的資料行,只包含一個指向資料行的指標。
就簡單的 SQL 查詢來看,分為 SELECT 和 WHERE 兩個部分,索引的建立也是以此為根據的,分為 複合索引 和 覆蓋索引。
事務管理
什麼是資料庫事務
資料庫的 事務(Transaction)是一種機制、一個操作序列,包含了一組資料庫操作命令,其執行的結果必須使資料庫從一種一致性狀態變到另一種一致性狀態。事務把所有的命令作為一個整體一起向系統提交或撤銷操作請求,即這一組資料庫命令要麼都執行,要麼都不執行,因此事務是一個不可分割的工作邏輯單元。如果任意一個操作失敗,那麼整組操作即為失敗,會回到操作前狀態或者是上一個節點。
因此,事務是保持 邏輯資料一致性 和 可恢復性 的重要利器。而鎖是實現事務的關鍵,可以保證事務的完整性和併發性
有哪些事務狀態
事務在其整個生命週期中會經歷不同的狀態,這些狀態也稱為 事務狀態。
- 活躍狀態:事務的第一個狀態,任何正在執行的事務都處於此狀態,所做的 更改 儲存在 主記憶體的緩衝區中
- 部分提交狀態:執行上次操作後,事務進入部分提交狀態。之所以是部分提交,是因為所做的更改仍然在主記憶體的緩衝區中。
- 失敗狀態:如果某個檢查在活動狀態下失敗,在活動狀態或部分提交狀態發生一些錯誤,並且事務無法進一步執行,則事務進入失敗狀態。
- 中止狀態:如果任何事務已達到失敗狀態,則恢復管理器將資料庫回滾到開始執行的原始狀態。
- 提交狀態:如果所有操作成功執行,則來自 部分提交狀態 的事務進入提交狀態。無法從此狀態回滾,它是一個新的 一致狀態。
事務的四大特性
事務具有 4 個特性,即原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和永續性(Durability),這 4 個特性通常簡稱為 ACID,關係型資料庫 需要遵循 ACID 規則。
原子性
事務是最小的執行單位,不可分割的(原子的)。事務的原子性確保動作要麼全部執行,要麼全部不執行。
以 銀行轉賬 事務為例,如果該事務提交了,則這兩個賬戶的資料將會更新;如果由於某種原因,事務在成功更新這兩個賬戶之前終止了,則不會更新這兩個賬戶的餘額,並且會 撤銷 對任何賬戶餘額的修改,回到此操作前狀態,即事務不能部分提交。
一致性
當事務完成時,資料必須處於一致狀態,多個事務對同一個資料讀取的結果是相同的。
以銀行轉賬事務事務為例。在事務開始之前,所有 賬戶餘額的總額處於一致狀態。在事務進行的過程中,一個賬戶餘額減少了,而另一個賬戶餘額尚未修改。因此,所有賬戶餘額的總額處於不一致狀態。但是當事務完成以後,賬戶餘額的總額再次恢復到一致狀態。
隔離性
併發訪問資料庫 時,一個使用者的事務不被其他事務所干擾,各個事務不干涉內部的資料。
修改資料的事務可以在另一個使用相同資料的事務開始之前訪問這些資料,或者在另一個使用相同資料的事務結束之後訪問這些資料。
永續性
一個事務被提交之後,它對資料庫中資料的改變是持久的,即使資料庫發生故障也不應該對其有任何影響。
如何實現事務的ACID特性
事務的 ACID 特性是由關聯式資料庫管理系統來實現的。
DBMS 採用 日誌 來保證事務的 原子性、一致性 和 永續性。日誌記錄了事務對資料庫所做的更新,如果某個事務在執行過程中發生錯誤,就可以根據日誌,撤銷事務對資料庫已做的更新,使資料庫退回到執行事務前的初始狀態。
DBMS 採用 鎖機制 來實現事務的隔離性。當多個事務同時更新資料庫中相同的資料時,只允許 持有鎖的事務 能更新該資料,其他事務必須等待,直到前一個事務釋放了鎖,其他事務才有機會更新該資料。
事務之間的相互影響
髒讀(Dirty Read)
一個事務讀取了另一個事務未提交的資料。
不可重複讀(Non-repeatable Read)
就是在一個事務範圍內,兩次相同的查詢會返回兩個不同的資料,這是因為在此間隔內有其他事務對資料進行了修改。
幻讀(Phantom Read)
幻讀是指當事務 不是獨立執行時 發生的一種現象,例如有一個事務對錶中的資料進行了修改,這種修改涉及到表中的全部資料行,同時,第一個事務也修改這個表中的資料,這種修改是向表中 插入一行新資料。那麼,第一個事務的使用者發現表中還有沒有修改的資料行,就好像發生了幻覺一樣。
丟失更新(Lost Update)
兩個事務同時讀取同一條記錄,事務 A 先修改記錄,事務 B 也修改記錄(B 是不知道 A 修改過),當 B 提交資料後, 其修改結果覆蓋了 A 的修改結果,導致事務 A 更新丟失。
什麼是事務的隔離級別
為了儘可能的避免上述事務之間的相互影響,從而達到事務的四大特性,SQL 標準定義了 4 種不同的事務隔離級別(TRANSACTION ISOLATION LEVEL),即 併發事務對同一資源的讀取深度層次,由低到高依次是 讀取未提交(READ-UNCOMMITTED)、讀取已提交(READ-COMMITTED)、可重複讀(REPEATABLE-READ)、可序列化(SERIALIZABLE),這 4 個級別與事務相互間影響問題對應如下:
讀取未提交
最低的隔離級別,一個事務可以讀到另一個事務未提交的結果,所有的併發事務問題都會發生。
讀取已提交
只有在事務提交後,其更新結果才會被其他事務看見,可以解決 髒讀問題,但是不可重複讀或幻讀仍有可能發生。Oracle 預設採用的是該隔離級別。
可重複讀
在一個事務中,對於同一份資料的讀取結果總是相同的,無論是否有其他事務對這份資料進行操作,以及這個事務是否提交,除非資料是被本身事務自己所修改。可以解決 髒讀、不可重複讀。MySQL 預設採用可重複讀隔離級別。
可序列化
事務 序列化執行,隔離級別最高,完全服從 ACID,犧牲了系統的併發性,也就是說,所有事務依次逐個執行,所以可以解決併發事務的所有問題。
鎖
鎖的分類
從資料庫系統的角度,鎖模式可分為以下6 種型別:
【1】共享鎖(S):又叫 他讀鎖。可以併發讀取資料,但不能修改資料。也就是說當資料資源上存在共享鎖時,所有的事務都不能對該資料進行修改,直到資料讀取完成,共享鎖釋放。
【2】排它鎖(X):又叫 獨佔鎖、寫鎖。對資料資源進行增刪改操作時,不允許其它事務操作這塊資源,直到排它鎖被釋放,從而防止同時對同一資源進行多重操作。
【3】更新鎖(U):防止出現 死鎖 的鎖模式,兩個事務對一個資料資源進行先讀取再修改的情況下,使用共享鎖和排它鎖有時會出現死鎖現象,而使用更新鎖就可以避免死鎖的出現。
資源的更新鎖一次只能分配給一個事務,如果需要對資源進行修改,更新鎖會變成排它鎖,否則變為共享鎖。
【4】意向鎖:表示 SQL Server 需要在 層次結構中的某些底層資源上 獲取共享鎖或排它鎖。例如,放置在 表級 的 共享意向鎖 表示事務打算在表中的頁或行上放置共享鎖。在表級設定意向鎖可防止另一個事務隨後在包含那一頁的表上獲取排它鎖。
意向鎖可以提高效能,因為 SQL Server 僅在 表級 檢查意向鎖來確定事務是否可以安全地獲取該表上的鎖,而無須檢查表中的每行或每頁上的鎖以確定事務是否可以鎖定整個表。
意向鎖包括意向共享 (IS)、意向排它 (IX) 以及與意向排它共享 (SIX)。
【5】架構鎖:在執行 依賴於表架構的操作 時使用。架構鎖的型別為:架構修改 (Sch-M) 和架構穩定性 (Sch-S),執行表的資料定義語言 (DDL)操作(例如新增列或除去表)時使用架構修改鎖,當編譯查詢時,使用架構穩定性鎖。
大容量更新鎖(BU):向表中大容量複製資料並指定了 TABLOCK 提示時使用。 大容量更新鎖允許程式將資料併發地大容量複製到同一表,同時防止其它不進行大容量複製資料的程式訪問該表。
事務隔離級別與鎖的關係
在 讀取未提交 隔離級別下,讀取資料不需要加 共享鎖,這樣就不會跟被修改的資料上的 排他鎖 衝突;
在 讀取已提交 隔離級別下,讀操作需要加 共享鎖,但是在語句執行完以後釋放共享鎖;
在 可重複讀 隔離級別下,讀操作需要加 共享鎖,但是在事務提交之前並不釋放共享鎖,也就是必須等待事務執行完畢以後才釋放共享鎖;
可序列化 是限制性最強的隔離級別,因為該級別 鎖定整個範圍的鍵,並一直持有鎖,直到事務完成。
什麼是死鎖?如何解決死鎖?
死鎖是指兩個或多個事務在同一資源上相互佔用,並請求鎖定對方的資源,從而導致惡性迴圈的現象。
常見的解決死鎖的方法
- 如果不同程式併發存取多個表,儘量約定 以相同的順序訪問表,可以大大降低死鎖機會;
- 在同一個事務中,儘可能做到 一次鎖定所需要的所有資源,減少死鎖產生概率;
- 對於非常容易產生死鎖的業務部分,可以嘗試使用 升級鎖定顆粒度,通過 表級鎖 定來減少死鎖產生的概率。
什麼是樂觀鎖和悲觀鎖?如何實現
DBMS 中的 併發控制 的任務是確保在 多個事務同時存取資料庫中同一資料 時不破壞事務的隔離性和統一性以及資料庫的統一性。樂觀併發控制(樂觀鎖)和悲觀併發控制(悲觀鎖)是併發控制主要採用的技術手段。
悲觀鎖:假定會發生併發衝突,遮蔽一切可能違反資料完整性的操作。在查詢完資料的時候就把事務鎖起來,直到提交事務。這對於長事務來講,可能會嚴重影響系統的併發處理能力。實現方式:使用資料庫中的鎖機制。
樂觀鎖:假設不會發生併發衝突,只在提交操作時檢查是否違反資料完整性。樂觀鎖適用於 讀多寫少 的應用場景,這樣可以提高吞吐量。實現方式:一般會使用版本號機制或 CAS 演算法實現。
常用SQL語句
SQL語句主要分為哪些類別
資料定義語言 DDL(Data Ddefinition Language):例如 CREATE,DROP,ALTER 等,對邏輯結構等有操作的,其中包括表結構,檢視和索引。
資料查詢語言 DQL(Data Query Language):即查詢操作,以 SELECT 關鍵字為主,各種簡單查詢、連線查詢等都屬於 DQL。
資料操縱語言 DML(Data Manipulation Language):例如 INSERT,UPDATE,DELETE 等,對資料進行操作的。DQL 與 DML共同構建了多數初級程式設計師常用的 增刪改查 操作,而查詢是較為特殊的一種,被劃分到 DQL 中。
資料控制功能 DCL(Data Control Language):例如 GRANT,REVOKE,COMMIT,ROLLBACK 等,對資料庫安全性、完整性等有操作的,可以簡單的理解為許可權控制等。
什麼是超鍵?什麼是主鍵?
超 鍵:在關係中,能唯一標識元組的屬性集稱為關係模式的超鍵。一個屬性可以作為一個超鍵,多個屬性組合在一起也可以作為一個超鍵。超鍵包含候選鍵和主鍵。
候選鍵:是最小超鍵,即沒有冗餘元素的超鍵。
主 鍵:資料庫表中對儲存資料物件予以 唯一和完整標識的資料列或屬性的組合。一個資料列只能有一個主鍵,且主鍵的取值不能缺失,即不能為空值(NULL)。
外 鍵:在一個表中存在的另一個表的主鍵稱此表的外來鍵,外來鍵可以有重複的, 可以是空值。外來鍵是用來和其他表建立聯絡用的。
SQL約束有哪幾種型別
約束是一種簡單地強加於表中一列或多列的限制,從而保證表中資料一致性(準確和可靠)。以下為六大約束:
非空約束(NOT NULL):保證該欄位值一定不為空;
預設約束(DEFAULT):保證欄位有預設值;
主鍵約束(PRIMARY KEY):標誌一列或者多列,並保證其值在表內的唯一性;
外來鍵約束(FOREIGN KEY):限制一列或多列中的值必須被包含在另一表的外來鍵列中,並且在級聯更新或級聯刪除規則建立後也可以限制其他表中的可用值;
唯一約束(UNIQUE): 限制一列或多列的值,保證欄位值在表內的唯一性,可以為空(主鍵約束是一種特殊型別的唯一約束);
檢查約束(CHECK):限制一列的可用值範圍。
char與varchar的區別
char 表示定長字串,長度是固定的,最多能存放的字元個數為 255,和編碼無關;而 varchar 表示可變長字串,長度是可變的,最多能存放的字元個數為 65532;
使用 char 時,如果插入資料的長度小於 char 的固定長度時,則用空格填充;
因為固定長度,char 的存取速度比 varchar 快很多,同時缺點是會佔用多餘空間,屬於空間換時間;
有哪些關聯查詢
在專案開發過程中,使用資料庫查詢語句時,有很多需求都是要涉及到較為複雜或者多表的連線查詢,需要關聯查詢實現。以下為總結的 MySQL 的五種關聯查詢。
交叉連線(CROSS JOIN)
除了在 FROM 子句中使用 逗號間隔連線的表 外,SQL 還支援另一種被稱為交叉連線的操作,它們都返回被連線的兩個表所有資料行的 笛卡爾積,返回到的資料行數等於第一個表中符合查詢條件的資料行數 乘以 第二個表中符合查詢條件的資料行數。惟一的不同在於,交叉連線分開列名時,使用 CROSS JOIN 關鍵字而不是逗號,即以下兩個表示式等價:
SELECT * FROM A, B
SELECT * FROM A CROSS JOIN B
內連線(INNER JOIN)
內連線分為三類,分別是 等值連線:ON A.id = B.id、不等值連線:ON A.id > B.id 和 自連線:SELECT * FROM A T1 INNER JOIN A T2 ON T1.id = T2.pid。
外連線(LEFT JOIN/RIGHT JOIN)
左外連線:以左表為主,先查詢出左表,按照 ON 後的關聯條件匹配右表,沒有匹配到的用 NULL 填充,可以簡寫成 LEFT JOIN;
右外連線:以右表為主,先查詢出右表,按照 ON 後的關聯條件匹配左表,沒有匹配到的用 NULL 填充,可以簡寫成 RIGHT JOIN;
聯合查詢(UNION 與 UNION ALL)
SELECT * FROM A UNION SELECT * FROM B UNION ...
聯合查詢就是把多個結果集集中在一起,UNION 前的結果為基準,需要注意的是聯合查詢的 列數要相等,相同的記錄行會合並;
如果使用 UNION ALL,不會合並重復的記錄行,所以效率更高。
全連線(FULL JOIN)
MySQL 本身不支援全連線,但可以通過聯合使用 LEFT JOIN、UNION 和 RIGHT JOIN 來實現。
SELECT * FROM A LEFT JOIN B ON A.id = B.id UNIONSELECT * FROM A RIGHT JOIN B ON A.id = B.id
什麼是子查詢
多條 MySQL 語句巢狀使用時,內部的 MySQL 查詢語句稱為子查詢。子查詢是一個 SELECT 語句,它巢狀在另一個 SELECT、SELECT…INTO 語句、INSERT…INTO 語句、DELETE 語句、 UPDATE 語句或巢狀在另一子查詢中。
MySQL 的子查詢是多表查詢的一個重要組成部分,常常和 連線查詢 一起使用,是多表查詢的基礎。
子查詢的分類
標量子查詢 查詢返回單一值的標量,如一個數字或一個字串,是子查詢中最簡單的形式。
列子查詢 子查詢返回的結果集是 N 行一列,該結果通常來自對錶的 某個欄位 查詢返回。
行子查詢 子查詢返回的結果集是一行 N 列,該結果通常是對錶的 某行資料 進行查詢而返回的結果集
表子查詢 子查詢返回的結果集是 N 行 N 列的一個表資料。
DROP、DELETE 與 TRUNCATE 的區別
UNION與UNION ALL的區別
UNION 用於把來自多個 SELECT 語句的結果組合到一個結果集合中,MySQL 會把結果集中 重複的記錄刪掉,而使用 UNION ALL,MySQL 會把所有的記錄返回,且效率高於 UNION 。
Redis常見面試題
Redis快取的特點
作為一個高效能的 Key-Value 資料庫,Redis 與其他 Key-Value 快取產品相比,有以下三個特點:
- Redis 執行在記憶體中但是可以 持久化到磁碟,重啟的時候可以再次載入進行使用。
- Redis 不僅僅支援簡單的 Key-Value 型別的資料,同時還提供 List,Set,Sorted set,hash 等資料結構的儲存。
- Redis 還支援資料的備份,即 Master-Slave 主從模式的資料備份。
Redis和MySQL的區別
首先,型別上的不同。MySQL 是關係型資料庫,採用表格的儲存方式,資料以行列組織:一個表中,每行是一個條目,都有且僅有此表的列所規定的屬性。MySQL 作為關係型資料庫遵守了傳統的資料庫的 ACID 原則 [①],這些要求在記錄重要資料(如使用者賬戶密碼,銀行戶頭等)時是必須的。Redis 是 NoSQL(非關係型資料庫),不嚴格遵守 ACID 原則,其採用 key-value 儲存組織資料,大量使用快取來讀寫資料,週期性地把更新資料寫入磁碟或在追加檔案中寫入修改操作,以及同步分佈資料庫等。這些特點為大大提高了 Redis 在極大併發量下的讀寫效能。由於不以表格方式組織資料,Redis 的資料擴充套件十分靈活方便。
功能不同。 MySQL 主要用於持久化的儲存資料到硬碟,功能強大,但是讀取速度較慢。而 Redis 將使用頻繁的資料儲存在快取中,快取的讀取速度快,能夠大大的提高執行效率,但是一般在使用中,Redis 快取的資料儲存時間是有限的。
執行機制不同。MySQL 作為典型的關係型資料庫,為了嚴格遵守 ACID 規則必須採取保守的快取策略,特別是為了保證資料的一致性,在保證資料可靠性的同時,犧牲了許多(特別是多併發情形下的)讀寫效能。而現在網際網路許多應用場景往往面臨了海量使用者的訪問請求,對資料庫並不需要非常強的一致性 [②]。如 Redis 等非關係型資料庫(NoSQL),往往以不再滿足 ACID 為代價求得更高的吞吐容量。NoSQL 通常採用極大的快取、分散式服務來提高讀寫效能。
Redis和MongoDB的區別
MongoDB 和 Redis 都是 NoSQL,採用結構型資料儲存,二者之間的主要區別如下所示:
- 效能方面,二者都很高,總體而言,TPS 方面 Redis 要大於 MongoDB;
- 可操作性上,MongoDB 支援豐富的資料表達、索引,最類似於關聯式資料庫,支援 豐富的查詢語言,操作比 Redis 更為便利;
- 記憶體及儲存方面,MongoDB 適合 大資料量儲存,依賴作業系統虛擬做記憶體管理,採用映象檔案儲存,記憶體佔有率比較高,Redis 2.0 後增加 虛擬記憶體特性,突破實體記憶體限制,資料可以設定時效性;
- 對於資料持久化和資料恢復,MongoDB 1.8 後,採用 binlog 方式(同 MySQL)支援持久化,增加了可靠性,而 Redis 依賴快照進行持久化、AOF 增強可靠性,但是增強可靠性的同時,也會影響訪問效能;
- 在資料一致性上,MongoDB 不支援事務,靠客戶端自身保證,而 Redis 支援事務,能保證事務中的操作按順序執行;
- 資料分析上,MongoDB 內建 資料分析功能(mapreduce),而 Redis 不支援資料分析;
- 應用場景不同,MongoDB 適合海量資料,側重於訪問效率的提升,而 Redis 適合於較小資料量,側重於效能。
Redis有哪些資料型別
String(字串),是 Redis 最基本的資料型別,二進位制安全的,可以包含任何資料,比如 JPG 圖片或者序列化的物件,最大能儲存 512 MB。
Hash(雜湊),是一個鍵值對(key => value)集合,特別適合用於儲存物件。
List(列表),Redis 列表是簡單的字串列表,按照插入順序排序,可以新增一個元素到列表的頭部(左邊)或者尾部(右邊)
Set(集合),是 String 型別的無序集合,通過雜湊表實現,添刪查詢操作的複雜度都是 O(1)。
Sorted set(有序集合),和 Set 一樣也是 String 型別元素的集合,且不允許元素重複, 不同的是每個元素都會關聯一個 Double 型別的分數(可重複), 通過此分數來為集合中的成員進行從小到大的排序。
如何實現Redis的定時機制
Redis 伺服器是一個 事件驅動程式,伺服器需要處理以下兩類事件:檔案事件 (伺服器對套接字操作的抽象)和 時間事件(伺服器對定時操作的抽象)。Redis 的定時機制就是藉助時間事件實現的。
一個時間事件主要由以下三個屬性組成:id,時間事件標識號;when,記錄時間事件的到達時間;timeProc,時間事件處理器,當時間事件到達時,伺服器就會呼叫相應的處理器來處理時間。一個時間事件根據時間事件處理器的返回值來判斷是定時事件還是週期性事件。
單執行緒Redis,為什麼如此高效
雖然 Redis 檔案事件處理器以單執行緒方式執行,但是通過使用 I/O 多路複用程式 來監聽多個套接字,檔案事件處理器既實現了高效能的網路通訊模型,又可以很好地與 Redis 伺服器中其他同樣以單執行緒執行的模組進行對接,這保持了 Redis 內部單執行緒設計的簡單性。
Redis有哪些資料淘汰策略
Redis 記憶體資料量達到一定限制的時候,就會實行資料淘汰策略(回收策略)。Redis 會根據 maxmemory-policy 配置策略,來決定具體的行為:
no-eviction:不刪除策略,達到最大記憶體限制時刻,如果需要更多記憶體,直接返回錯誤資訊;
allkey-lru:從所有 Key 的雜湊表(server.db[i].dict)中隨機挑選多個 Key,然後在選到的 Key 中利用 lru 演算法淘汰最近最少使用的資料;
volatile-lru:從已設定過期時間的雜湊表(server.db[i].expires)中隨機挑選多個 Key,然後在選到的 Key 中用 lru 演算法淘汰最近最少使用的資料;
volatile-random:從已設定過期時間的雜湊表(server.db[i].expires)中隨機挑選 Key淘汰掉;
allkey-random:從所有的 Key 的雜湊表(server.db[i].dict)中隨機挑選資料淘汰;
volatile-ttl:從已設定過期時間的雜湊表(server.db[i].expires)中隨機挑選多個 Key,然後在選到的 Key 中選擇剩餘時間最短的資料淘汰掉。
對Redis的理解
Redis,全稱為 Remote Dictionary Server,本質上是一個 Key-Value 型別的記憶體資料庫,整個資料庫統統載入在記憶體當中進行操作,定期通過非同步操作把資料庫資料寫入磁碟或把修改操作寫入追加的記錄檔案,並且在此基礎上實現 Master-Slave(主從)同步。它支援儲存的 Value 型別多樣,包括 String(字串)、List(連結串列)、Set(集合)、zset(sorted set —— 有序集合)和 Hash(雜湊型別),這些資料型別都支援 push/pop、add/remove 及取交集並集和差集及更豐富的操作,而且這些操作都是原子性的。
Redis 的主要缺點是資料庫容量受到實體記憶體的限制,不能用作海量資料的高效能讀寫,因此 Redis 適合的場景主要侷限在較小資料量的高效能操作和運算上。
資料庫優化
資料庫結構優化
在資料庫設計中,需要考慮資料冗餘、查詢和更新的速度、欄位的資料型別是否合理等多方面的內容。可通過以下方式進行資料庫結構的優化:
將欄位很多的表分解成多個表 對於欄位較多的表,如果有些欄位的使用頻率很低,可以將這些欄位分離出來形成新表。
增加中間表 對於需要經常 聯合查詢 的表,通過建立中間表以提高查詢效率,具體地,將需要通過聯合查詢的資料插入到中間表中,然後將原來的聯合查詢改為對中間表的查詢。
增加冗餘欄位 眾所皆知,設計資料表時應儘量遵循正規化理論的規約,儘可能的減少冗餘欄位,讓資料庫設計看起來精緻、優雅。但是,表的規範化程度越高,表和表之間的關係越多,需要連線查詢的情況也就越多,效能也就越差,所以合理的加入冗餘欄位可以提高查詢速度。
MySQL 資料庫 CPU 使用率飆升到 500% 的話,如何處理?
當 CPU 飆升到 500% 時,先用作業系統命令 top 命令觀察是不是 mysqld 佔用導致的,如果不是,找出佔用高的程式,並進行相關處理。
如果是 mysqld 造成的,通過 SHOW PROCESSLIST 檢視正在執行的執行緒,是不是有消耗資源的 SQL 在執行,找出其中消耗高的 SQL,看看執行計劃是否準確, index 是否缺失,或者是資料量太大造成。
然後 kill 掉這些執行緒(同時觀察 CPU 使用率是否下降),等進行相應的調整(比如說加索引、改 SQL、改記憶體引數)之後,再重新跑這些 SQL。
若每個 SQL 消耗資源都不多,只是同一時間大量的 session 連進來導致 CPU 飆升,這種情況就需要分析為何連線數會激增,再做出相應的調整,比如說限制連線數等。
為什麼要分庫分表
資料庫中的資料量不一定是可控的,隨著時間和業務的發展,庫中的表會越來越多,表中的資料量也會越來越大,相應地資料操作,例如 增刪改查的開銷 也會越來越大;另外,若不進行分散式部署,而一臺伺服器的 資源 (CPU、磁碟、記憶體、IO 等)是有限的,最終資料庫所能承載的資料量、資料處理能力都將遭遇瓶頸。所以,從 效能 和 可用性 角度考慮,會進行資料庫拆分處理,具體地說,把原本儲存於一個庫的資料分塊儲存到多個庫上,把原本儲存於一個表的資料分塊儲存到多個表上,即 分庫分表。
分庫分表的具體實施策略
分庫分表存在哪些問題
進行分庫分表操作後,可能會面臨以下幾類問題:
- 事務問題 分庫分表後,就成了分散式事務。如果依賴資料庫本身的分散式事務管理功能去執行事務,將付出高昂的效能代價; 如果由應用程式去協助控制,形成程式邏輯上的事務,又會造成程式設計方面的負擔。
- 跨庫跨表的 JOIN 問題 在執行了分庫分表之後,難以避免會將原本邏輯關聯性很強的資料劃分到不同的表、不同的庫上,這時,表的關聯操作將受到限制,我們無法 JOIN 位於不同分庫的表,也無法 JOIN 分表粒度不同的表,結果原本一次查詢能夠完成的業務,可能需要多次查詢才能完成。
- 額外的資料管理負擔和資料運算壓力 額外的資料管理負擔,最為常見的是資料的 定位問題 和資料的 增刪改查 的重複執行問題,這些都可以通過應用程式來解決,但必然會引起額外的邏輯運算。
什麼是MySQL主從複製
主從複製是指將 主資料庫(Master)中的 DDL 和 DML 操作通過二進位制日誌傳輸到 從資料庫(Slave) 上,然後將這些日誌重新執行(重做),從而使得從資料庫的資料與主資料庫保持一致。MySQL 支援單向、非同步複製,複製過程中一個伺服器充當主伺服器,而一個或多個其它伺服器充當從伺服器。
主從複製的作用有:
- 當主資料庫出現問題時,可以切換到從資料庫;
- 可以進行資料庫層面的讀寫分離,實現負載均衡;
- 可以在從資料庫上進行實時資料備份。
MySQL主從複製的工作原理
MySQL 的主從複製是一個 非同步 的複製過程(一般情況下感覺是實時的),資料將從一個 MySQL 資料庫(Master)複製到另外一個 MySQL 資料庫(Slave),在 Master 與 Slave 之間實現整個主從複製的過程是由三個執行緒參與完成的,其中有兩個執行緒(SQL 執行緒和 I/O 執行緒)在 Slave 端,另外一個執行緒( I/O 執行緒)在 Master 端。
基本原理流程
Master 端:開啟二進位制日誌(binlog )記錄功能 —— 記錄下所有改變了資料庫資料的語句,放進 Master 的 binlog 中;
Slave 端:開啟一個 I/O 執行緒 —— 負責從 Master上拉取 binlog 內容,放進自己的中繼日誌(Relay log)中;
Slave 端:SQL 執行執行緒 —— 讀取 Relay log,並順序執行該日誌中的 SQL 事件。
MySQL讀寫分離的實現方案
MySQL 讀寫分離的實現方式主要基於 主從複製,通過 路由的方式 使應用對資料庫的寫請求只在 Master 上進行,讀請求在 Slave 上進行。
具體地,有以下四種實現方案:
方案一:基於 MySQL proxy 代理
在應用和資料庫之間增加 代理層,代理層接收應用對資料庫的請求,根據不同請求型別(即是讀 read 還是寫 write)轉發到不同的例項,在實現讀寫分離的同時可以實現負載均衡。MySQL 的代理最常見的是 mysql-proxy、cobar、mycat、Atlas 等。
方案二:基於應用內路由
基於應用內路由的方式即為在應用程式中實現,針對不同的請求型別去不同的例項執行 SQL。
具體實現可基於 spring 的 aop:用 aop 來攔截 spring 專案的 dao 層方法,根據方法名稱就可以判斷要執行的型別,進而動態切換主從資料來源。
方案三:基於 MySQL-Connector-Java 的 JDBC 驅動方式
Java 程式通過在連線 MySQL 的 JDBC 中配置主庫與從庫等地址,JDBC 會自動將讀請求傳送給從庫,將寫請求傳送給主庫,此外, MySQL 的 JDBC 驅動還能夠實現多個從庫的負載均衡。
方案四:基於 sharding-jdbc 的方式
sharding-sphere 是強大的讀寫分離、分表分庫中介軟體,sharding-jdbc 是 sharding-sphere 的核心模組。