Web后端數據庫一般用mysql。
數據庫基礎
Q:數據庫三范式是什么?
第一范式:列不可再分
第二范式:行可以唯一區分,主鍵約束
第三范式:表的非主屬性不能依賴與其他表的非主屬性 外鍵約束
且三大范式是一級一級依賴的,第二范式建立在第一范式上,第三范式建立第一第二范式上。
Q:數據庫引擎有哪些?他們有什么區別?
區別:
- InnoDB支持事務,MyISAM不支持,對於InnoDB每一條SQL語言都默認封裝成事務,自動提交,這樣會影響速度,所以最好把多條SQL語言放在begin和commit之間,組成一個事務;
- InnoDB支持外鍵,而MyISAM不支持。對一個包含外鍵的InnoDB表轉為MYISAM會失敗;
- InnoDB是聚集索引,數據文件是和索引綁在一起的,必須要有主鍵,通過主鍵索引效率很高。但是輔助索引需要兩次查詢,先查詢到主鍵,然后再通過主鍵查詢到數據。因此,主鍵不應該過大,因為主鍵太大,其他索引也都會很大。而MyISAM是非聚集索引,數據文件是分離的,索引保存的是數據文件的指針。主鍵索引和輔助索引是獨立的。
- InnoDB不保存表的具體行數,執行select count(*) from table時需要全表掃描。而MyISAM用一個變量保存了整個表的行數,執行上述語句時只需要讀出該變量即可,速度很快;
- Innodb不支持全文索引,而MyISAM支持全文索引,查詢效率上MyISAM要高;
Q:數據庫事務是什么?
多條sql語句,要么全部成功,要么全部失敗。
Q:數據庫事務有哪些特性?
數據庫事務特性:原子性(Atomic)、一致性(Consistency)、隔離性(Isolation)、持久性(Durabiliy)。簡稱ACID。
原子性:組成一個事務的多個數據庫操作是一個不可分割的原子單元,只有所有操作都成功,整個事務才會提交。任何一個操作失敗,已經執行的任何操作都必須撤銷,讓數據庫返回初始狀態。
一致性:事務操作成功后,數據庫所處的狀態和它的業務規則是一致的。即數據不會被破壞。如A轉賬100元給B,不管操作是否成功,A和B的賬戶總額是不變的。
隔離性:在並發數據操作時,不同的事務擁有各自的數據空間,它們的操作不會對彼此產生干擾
持久性:一旦事務提交成功,事務中的所有操作都必須持久化到數據庫中。
Q:數據庫的隔離級別有哪些?
讀未提交(read-uncommitted)讀提交(read-committed) 可重復讀(repeatable-read)串行化(serializable)。
Q:mysql默認的數據庫隔離級別是什么?
可重復讀(repeatable-read)
Q:mysql的可重復讀,是怎么實現的?
使用的的一種叫MVCC的控制方式 ,即Mutil-Version Concurrency Control,多版本並發控制,類似於樂觀鎖的一種實現方式。
Q:MVCC的實現方式是怎樣的?
InnoDB在每行記錄后面保存了隱藏的列,其中包括版本號,當數據被修改時,版本號加1。
在讀取事務開始時,系統會給當前讀事務一個版本號,事務會讀取版本號<=當前版本號的數據。
此時如果其他寫事務修改了這條數據,那么這條數據的版本號就會加1,從而比當前讀事務的版本號高,讀事務自然而然的就讀不到更新后的數據了。
Q:為什么mysql要將隔離級別設置為可重復讀?為什么mysql不像oracle一樣,像事務隔離級別設置為讀提交?
讀提交的隔離級別,會出現不可重復讀。
在讀提交的隔離級別下,比如當事務A查詢帳戶余額為100,事務B將余額取出50並提交,那么事務A再次查詢賬戶時余額為50,這個就是不可重復讀。
那既然如此,為什么oracle要把事務隔離級別設置為讀提交呢?
mysql為什么要將隔離級別設置為可重復讀,更深層的原因見:https://blog.csdn.net/m0_37774696/article/details/88951846
Q:不同的數據庫隔離級別會導致哪些問題?
數據庫中的數據可能同時被多個事務訪問,如果沒有采取必要的隔離措施,就會導致各種並發問題,破壞數據的完整性。
1.臟讀:A事務讀取B事務尚未提交的更改數據,並在這個數據的基礎上進行操作。如果恰巧B事務回滾,那么A事務讀取到的數據是根本不被承認的。
讀未提交的隔離級別,會出現臟讀。
2.不可重復讀:A事務讀取了B事務已經提交的更改數據。(注意,是更改)
讀提交的隔離級別,會出現不可重復讀。
3.幻讀:A事務讀取了B事務已經提交的新增數據。(注意,是新增)
可重復讀的隔離級別,會出現幻讀。
概念講解:https://www.cnblogs.com/expiator/p/9626123.html
操作示例:https://juejin.im/post/5d8b2a9c518825091471fe2c
sql
Q:union和union all的區別是什么?
union:對兩個結果集進行並集操作,不包括重復行,同時進行默認規則的排序;
union all:對兩個結果集進行並集操作,包括重復行,不進行排序;
Q:講一下各種join的區別。
JOIN: 如果表中有至少一個匹配,則返回行
INNER JOIN 與 JOIN 是相同的。
LEFT JOIN: 即使右表中沒有匹配,也從左表返回所有的行
RIGHT JOIN: 即使左表中沒有匹配,也從右表返回所有的行
FULL JOIN: 只要其中一個表中存在匹配,就返回行
Q:講一下explain。
它可以對 SELECT 語句進行分析,並輸出 SELECT 執行的詳細信息,方便針對性地優化。
查詢結果的字段如下:
select_type: SELECT 查詢的類型。包括SIMPLE、PRIMARY、UNION、UNION RESULT等
table: 查詢的是哪個表
partitions: 匹配的分區
type: 類型。type值為all,表示全表掃描。type值為const,說明使用了主鍵索引。
不同的 type 類型的性能關系如下:
ALL < index < range ~ index_merge < ref < eq_ref < const < system。
possible_keys: 此次查詢中可能選用的索引
key: 此次查詢中確切使用到的索引.
ref: 哪個字段或常數與 key 一起被使用
rows: 顯示此查詢一共掃描了多少行. 這個是一個估計值.
filtered: 表示此查詢條件所過濾的數據的百分比
extra: 額外的信息
參考資料:https://segmentfault.com/a/1190000008131735
sql優化
Q:平常使用過哪些sql優化的手段?
1、查詢語句中不要使用select *
2、盡量減少子查詢,使用關聯查詢(left join,right join,inner join)替代
3、減少使用IN或者NOT IN ,使用exists,not exists或者關聯查詢語句替代
4、or 的查詢盡量用 union或者union all 代替(在確認沒有重復數據或者不用剔除重復數據時,union all會更好)
5、應盡量避免在 where 子句中使用!=或<>操作符,否則將引擎放棄使用索引而進行全表掃描。
6、應盡量避免在 where 子句中對字段進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如:
select id from t where num is null
可以在num上設置默認值0,確保表中num列沒有null值,然后這樣查詢:
select id from t where num=0
索引
Q:有哪些數據結構的索引?
B+ Tree索引、哈希索引(Hash Index)、空間數據索引(R-Tree)、全文索引(Full Text)
Q:有哪些類型的索引?
普通索引,唯一索引,主鍵索引與組合索引
普通索引:僅加速查詢
唯一索引:加速查詢 + 列值唯一(可以有null)
主鍵索引:加速查詢 + 列值唯一(不可以有null)+ 表中只有一個
組合索引:多列值組成一個索引,專門用於組合搜索,其效率大於索引合並
全文索引:對文本的內容進行分詞,進行搜索
Q:唯一索引和主鍵索引有什么區別?
唯一索引可以是null,主鍵索引不可以有null。
Q:索引的數據結構。
B+樹。Mysql支持Hash索引和B+樹索引兩種
Q:B+樹和B樹的區別有哪些?
B+ Tree 和 B Tree 不同,B+ Tree 中,只能將數據存儲在葉子結點中,內部節點將只包含指針,而 B Tree 可以將數據存儲在內部的葉節點中。因此 B+ Tree 的關鍵優勢是中間節點不包含數據,因此 B+ Tree 的大小遠小於 B Tree,並且可以將更多數據存儲到存儲器中。另外,B+ Tree 的每一個葉子節點包含了到相鄰的節點的鏈接,這樣可以快速地進行范圍遍歷。
Q:使用索引為什么可以加快數據庫的檢索速度啊?
將無序的數據變成有序(相對)。沒有用索引我們是需要遍歷雙向鏈表來定位對應的頁,現在通過“目錄”就可以很快地定位到對應的頁上了。
類比翻字典。。查了索引,知道是在第幾頁,直接去第幾頁找就行了。不要從頭開始翻。
Q:為什么說索引會降低插入、刪除、修改等維護任務的速度。
B+樹是一顆平衡樹,如果我們對這顆樹增刪改的話,那肯定會破壞它的原有結構。
要維持平衡樹,就必須做額外的工作。正因為這些額外的工作開銷?導致索引會降低增刪改的速度
Q:索引的最左匹配原則指的是什么?
Q:Hash索引和B+樹索引有什么區別?主流的使用哪一個比較多?InnoDB存儲都支持嗎?
Q:組合索引是怎么存儲數據的?
組合索引也是一棵B+樹,不同的是組合索引的鍵值不是1個,而是大於等於2個。
Q:組合索引有哪些使用規則?
Q:聚集索引和非聚集索引有什么區別?
簡單概括:
聚集索引就是以主鍵創建的索引;
非聚集索引就是以非主鍵創建的索引。
區別:
聚集索引在葉子節點存儲的是表中的數據
非聚集索引在葉子節點存儲的是主鍵和索引列
使用非聚集索引查詢出數據時,拿到葉子上的主鍵再去查到想要查找的數據。(拿到主鍵再查找這個過程叫做回表)
Q:索引的最左前綴原則是什么?
如果不是按照索引的最左列開始查找,則無法使用索引;
不能跳過聯合索引中的某些列;
如果查詢中有某個列的范圍查詢,則其右邊所有列都無法使用索引優化查找;
Q:索引什么時候不生效?
Q:索引優化策略。
1.最左前綴匹配原則。
2.主鍵外鍵一定要建索引
3. 對 where,on,group by,order by 中出現的列使用索引
4.盡量選擇區分度高的列作為索引,區分度的公式是count(distinct col)/count(*),表示字段不重復的比例,比例越大我們掃描的記錄數越少,唯一鍵的區分度是1,而一些狀態、性別字段可能在大數據面前區分度就是0
5.對較小的數據列使用索引,這樣會使索引文件更小,同時內存中也可以裝載更多的索引鍵
6.索引列不能參與計算,保持列“干凈”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很簡單,b+樹中存的都是數據表中的字段值,但進行檢索時,需要把所有元素都應用函數才能比較,顯然成本太大。所以語句應該寫成create_time = unix_timestamp(’2014-05-29’);
7.為較長的字符串使用前綴索引
8.盡量的擴展索引,不要新建索引。比如表中已經有a的索引,現在要加(a,b)的索引,那么只需要修改原來的索引即可
9.不要過多創建索引, 權衡索引個數與DML之間關系,DML也就是插入、刪除數據操作。這里需要權衡一個問題,建立索引的目的是為了提高查詢效率的,但建立的索引過多,會影響插入、刪除數據的速度,因為我們修改的表數據,索引也需要進行調整重建
10.對於like查詢,”%”不要放在前面。
SELECT * FROM t_order WHERE uname LIKE '編程%' -- 走索引
SELECT * FROM t_order WHERE uname LIKE '%編程%' -- 不走索引
可以用instr代替左模糊。
instr(title,'name')>0 相當於 title like '%name%'
instr(title,'name')=1 相當於 title like 'name%'
instr(title,'name')=0 相當於 title not like '%name%'
11.查詢where條件數據類型不匹配也無法使用索引。字符串與數字比較不使用索引;
Q:有一個性別字段,男和女,這個字段需要加索引嗎?
離散度低的字段,沒有必要加索引。
鎖
Q:講一下悲觀鎖、樂觀鎖。
悲觀鎖是指總是假設最壞的情況,每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖。具有獨占性和排他性。比如,行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。
樂觀鎖不是數據庫層面上的鎖,是需要自己手動去加的鎖。一般我們添加一個版本字段version來實現。
Q:講一下共享鎖、排它鎖。
共享鎖:指的就是對於多個不同的事務,對同一個資源共享同一個鎖。相當於對於同一把門,它擁有多個鑰匙一樣。
在執行語句后面加上lock in share mode就代表對某些資源加上共享鎖了。
排它鎖:排它鎖,就是指對於多個不同的事務,對同一個資源只能有一把鎖。
在需要執行的語句后面加上for update就可以了。
binlog
Q:講一下binlog、redoLog、undoLog
二進制日志(binlog)。作用:用於復制,在主從復制中,從庫利用主庫上的binlog進行重播,實現主從同步。
回滾日志(undo log)。作用:保存了事務發生之前的數據的一個版本,可以用於回滾。
使用undolog來實現原子性,如果事務執行過程中出錯或者用戶執行了rollback,系統通過undolog日志返回事務開始的狀態。
redo log是InnoDB存儲引擎層的日志,又稱重做日志文件,用於記錄事務操作的變化,記錄的是數據修改之后的值,不管事務是否提交都會記錄下來。
使用redolog來實現持久性,只要redolog日志持久化了,當系統崩潰,即可通過redolog把數據恢復。
Q:binlog有幾種格式?
statement:記錄的是修改SQL語句。
row:記錄的是每行實際數據的變更。
mixed:statement和row模式的混合。
主從架構
Q:為什么要做主從架構?
讀寫分離,主機寫,從機讀。提高數據庫性能,扛更高的並發。
Q:講一下主從復制。
主庫創建一個binlog dump thread線程,把binlog的內容發送到從庫。
從庫創建一個I/O線程,讀取主庫傳過來的binlog內容並寫入到relay log。
從庫還會創建一個SQL線程,從relay log里面讀取內容,並將更新內容寫入到slave的db(也就是重新在從庫里跑一次insert/update語句)。
參考資料:https://blog.csdn.net/darkangel1228/article/details/80003967
Q:主從復制延遲怎么解決?
如果只是部分業務出現延遲,可以直接用主庫去讀。這個是比較粗暴的方法。
其他的方法:
a.分庫,降低並發量。
即對於分庫,則根據業務內聚性,拆分為幾個相對獨立的子服務,各個子服務使用獨立的數據庫分開部署,則可以將寫操作分散到各個數據庫,各個數據庫的復制流量相對較小,從而通過分而治之的方法來降低整體的延遲。
b.打開Mysql的並行復制,多庫並行復制。
分庫分表
Q:分庫分表怎么分?
水平分表,垂直分表
Q:分庫分表有哪些中間件?
ShardingJdbc、MyCat
Q:分表策略有哪些?
Hash分表:對某個字段進行hash分表。比如user表可以對user_id值進行hash后拆分成user_1,user_2。。查詢后根據id的hash值找到對應的表就可以了。
待補充。
參考資料:
數據庫面試題--開發者必看
數據庫要點--索引和鎖