MySQL資料庫基礎知識及操作
MySQL資料庫基礎知識及操作
MySQL伺服器邏輯架構圖
問:innodb引擎執行一條select語句的過程?
聯結器
第一步,你會先連線到這個資料庫上,這時候接待你的就是聯結器。聯結器負責跟客戶端建立連線、獲取許可權、維持和管理連線。連線命令:
mysql -h$ip -P$port -u$user -p
連線命令中的 mysql 是客戶端工具,用來跟服務端建立連線。在完成經典的 TCP 握手後,聯結器就要開始認證你的身份,這個時候用的就是你輸入的使用者名稱和密碼。
- 如果使用者名稱或密碼不對,你就會收到一個"Access denied for user"的錯誤,然後客戶端程式結束執行。
- 如果使用者名稱密碼認證通過,聯結器會到許可權表裡面查出你擁有的許可權。之後,這個連線裡面的許可權判斷邏輯,都將依賴於此時讀到的許可權。
這就意味著,一個使用者成功建立連線後,即使你用管理員賬號對這個使用者的許可權做了修改,也不會影響已經存在連線的許可權。修改完成後,只有再新建的連線才會使用新的許可權設定。
長連線和短連線
資料庫裡面,長連線是指連線成功後,如果客戶端持續有請求,則一直使用同一個連線。短連線則是指每次執行完很少的幾次查詢就斷開連線,下次查詢再重新建立一個。
建立連線的過程通常是比較複雜的,所以我建議你在使用中要儘量減少建立連線的動作,也就是儘量使用長連線。
mysql執行過程中一些資料繫結在連線物件中,因此全部都用長連線長時間不斷開會導致記憶體佔用太多。所以如果長連線累積下來,可能導致記憶體佔用太大,被系統強行殺掉(OOM),從現象看就是 MySQL 異常重啟了。
怎麼解決這個問題呢?你可以考慮以下兩種方案。
- 定期斷開長連線。使用一段時間,或者程式裡面判斷執行過一個佔用記憶體的大查詢後,斷開連線,之後要查詢再重連。
- 如果你用的是 MySQL 5.7 或更新版本,可以在每次執行一個比較大的操作後,通過執行 mysql_reset_connection 來重新初始化連線資源。這個過程不需要重連和重新做許可權驗證,但是會將連線恢復到剛剛建立完時的狀態。
查詢快取
但是大多數情況下我會建議你不要使用查詢快取,為什麼呢?因為查詢快取往往弊大於利。查詢快取的失效非常頻繁,只要有對一個表的更新,這個表上所有的查詢快取都會被清空。因此很可能你費勁地把結果存起來,還沒使用呢,就被一個更新全清空了。對於更新壓力大的資料庫來說,查詢快取的命中率會非常低。除非你的業務就是有一張靜態表,很長時間才會更新一次。比如,一個系統配置表,那這張表上的查詢才適合使用查詢快取。
需要注意的是,MySQL 8.0 版本直接將查詢快取的整塊功能刪掉了,也就是說 8.0 開始徹底沒有這個功能了。
分析器
如果沒有命中查詢快取,就要開始真正執行語句了。首先,MySQL 需要知道你要做什麼,因此需要對 SQL 語句做解析。分析器先會做“詞法分析”。你輸入的是由多個字串和空格組成的一條 SQL 語句,MySQL 需要識別出裡面的字串分別是什麼,代表什麼。
優化器
經過了分析器,MySQL 就知道你要做什麼了。在開始執行之前,還要先經過優化器的處理。優化器是在表裡面有多個索引的時候,決定使用哪個索引;或者在一個語句有多表關聯(join)的時候,決定各個表的連線順序。
執行器
執行器MySQL 通過分析器知道了你要做什麼,通過優化器知道了該怎麼做,於是就進入了執行器階段,開始執行語句。
MySQL 整體來看,其實就有兩塊:一塊是 Server 層,它主要做的是 MySQL 功能層面的事情;還有一塊是引擎層,負責儲存相關的具體事宜
儲存引擎
常見的有MyISAM和InnoDB。
InnoDB 是支援行鎖的,這也是 MyISAM 被 InnoDB 替代的重要原因之一。
事務
MySQL預設採用自動提交(AUTOCOMMIT)模式。也就是說,如果不是顯式地開始一個事務,則每個查詢都被當做一個事務執行提交操作。
學了很久但是記不住的MYSQL事務以及隔離級別:
https://blog.csdn.net/qq_42240540/article/details/115541067
一、事務的基本要素(ACID)
1、原子性(Atomicity):事務開始後所有操作,要麼全部做完,要麼全部不做,不可能停滯在中間環節。事務執行過程中出錯,會回滾到事務開始前的狀態,所有的操作就像沒有發生一樣。也就是說事務是一個不可分割的整體,就像化學中學過的原子,是物質構成的基本單位。
2、一致性(Consistency):事務開始前和結束後,資料庫的完整性約束沒有被破壞 。比如A向B轉賬,不可能A扣了錢,B卻沒收到。
3、隔離性(Isolation):同一時間,只允許一個事務請求同一資料,不同的事務之間彼此沒有任何干擾。比如A正在從一張銀行卡中取錢,在A取錢的過程結束前,B不能向這張卡轉賬。
4、永續性(Durability):事務完成後,事務對資料庫的所有更新將被儲存到資料庫,不能回滾。
注:MySQL中ACID靠什麼保證的?
A:原⼦性由undo log⽇志保證,它記錄了需要回滾的⽇志資訊,事務回滾時撤銷已經執⾏成功的sql
C:⼀致性由其他三⼤特性保證、程式程式碼要保證業務上的⼀致性
I:隔離性由MVCC來保證
D:永續性由記憶體+redo log來保證,mysql修改資料同時在記憶體和redo log記錄這次操作,宕機的時候可
以從redo log恢復
二、事務的併發問題
1、髒讀:事務A讀取了事務B更新的資料,然後B回滾操作,那麼A讀取到的資料是髒資料
2、不可重複讀:事務 A 多次讀取同一資料,事務 B 在事務A多次讀取的過程中,對資料作了更新並提交,導致事務A多次讀取同一資料時,結果 不一致。
3、幻讀:系統管理員A將資料庫中所有學生的成績從具體分數改為ABCDE等級,但是系統管理員B就在這個時候插入了一條具體分數的記錄,當系統管理員A改結束後發現還有一條記錄沒有改過來,就好像發生了幻覺一樣,這就叫幻讀。
小結:不可重複讀的和幻讀很容易混淆,不可重複讀側重於修改,幻讀側重於新增或刪除。解決不可重複讀的問題只需鎖住滿足條件的行,解決幻讀需要鎖表
三、MySQL事務隔離級別
事務隔離級別 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|
未提交讀(read-uncommitted) | 是 | 是 | 是 |
提交讀(read-committed) | 否 | 是 | 是 |
可重複讀(repeatable-read) | 否 | 否 | 是 |
序列化(serializable) | 否 | 否 | 否 |
未提交讀:就是指事務不用進行資料提交就可以被其他事務讀到。
不可重複讀(提交讀):事務進行資料提交後才能被其他事務讀到。問題是當前事務中存在兩次同樣的查詢之間其他事務對資料修改並提交後當前事務會得到不同的結果,所以叫不可重複讀。
可重複讀:解決了不可重複讀,原理是當前事務要使用資料提交才可見(重新整理)其他事務的資料修改。但問題是可能存在幻讀,幻讀指事務讀取某個範圍過程中有其他事務插入了新的資料行,該新資料會被當前查詢過程讀出來;而如果當前事務再讀取該範圍時並且兩次查詢中未使用資料提交,則會發現該資料行不見了,就好像出現了幻覺。。(顧名思義得來的把。。。總算是認真理解了下這個意思了。這書講的也不詳細啊)。但InnoDB儲存引擎通過多版本併發控制(MVCC)解決了幻讀的問題。
可序列化:這是最高的隔離級別,它強制事務都是序列執行的,使之不可能相互衝突,從而解決幻讀問題。換言之,它是在每個讀的資料行上加上共享鎖(讀鎖)。在這個級別,可能導致大量的超時現象和鎖競爭。
注:
對於可重複讀,查詢只承認在事務啟動前就已經提交完成的資料;可重複讀的核心就是一致性讀(consistent read)
對於讀提交,查詢只承認在語句啟動前就已經提交完成的資料;
參考:https://www.cnblogs.com/wyaokai/p/10921323.html
MySQL中MVCC(多版本控制)的正確開啟方式
多版本控制(Multiversion Concurrency Control): 指的是一種提高併發的技術。同一條記錄在系統中可以存在多個版本,就是資料庫的多版本併發控制(MVCC)。
最早的資料庫系統,只有讀讀之間可以併發,讀寫,寫讀,寫寫都要阻塞。引入多版本之後,只有寫寫之間相互阻塞,其他三種操作都可以並行,這樣大幅度提高了InnoDB的併發度。換言之,就是為了查詢一些正在被另一個事務更新的行,並且可以看到它們被更新之前的值。這是一個可以用來增強併發性的強大的技術,因為這樣一來的話查詢就不用等待另一個事務釋放鎖。關鍵:解決了讀-寫衝突
MySQL中MVCC在 Read Committed 和 Repeatable Read兩個隔離級別下工作。
MySQL的InnoDB儲存引擎預設事務隔離級別是RR(可重複讀),是通過 "行級鎖+MVCC"一起實現的。而 MVCC 的實現依賴:隱藏欄位、Read View、Undo log。
實現
使用兩個隱藏的列,行版本號row trx_id(最近更新時間),行刪除標識(刪除時間)。結合undo log日誌實現的。
InnoDB 裡面每個事務有一個唯一的事務 ID,叫作 transaction id。它是在事務開始的時候向 InnoDB 的事務系統申請的,是按申請順序嚴格遞增的。而每行資料也都是有多個版本的。每次事務更新資料的時候,都會生成一個新的資料版本,並且把 transaction id 賦值給這個資料版本的事務 ID,記為 row trx_id。同時,舊的資料版本要保留,並且在新的資料版本中,能夠有資訊可以直接拿到它。也就是說,資料表中的一行記錄,其實可能有多個版本 (row),每個版本有自己的 row trx_id。如圖所示,就是一個記錄被多個事務連續更新後的狀態。
圖中虛線框裡是同一行資料的 4 個版本,當前最新版本是 V4,k 的值是 22,它是被 transaction id 為 25 的事務更新的,因此它的 row trx_id 也是 25。
語句更新會生成 undo log(回滾日誌)。那麼,undo log 在哪呢?實際上,圖 2 中的三個虛線箭頭,就是 undo log;而 V1、V2、V3 並不是物理上真實存在的,而是每次需要的時候根據當前版本和 undo log 計算出來的。比如,需要 V2 的時候,就是通過 V4 依次執行 U3、U2 算出來。
簡單來說可重複讀情況下,一個數據版本,對於一個事務檢視來說,除了自己的更新總是可見以外,有三種情況:
- 版本未提交,不可見;
- 版本已提交,但是是在檢視建立後提交的,不可見;
- 版本已提交,而且是在檢視建立前提交的,可見。
Read View(快照機制):一致性讀
當執行select操作是innodb預設會執行快照讀,會記錄下這次select後的結果,之後select 的時候就會返回這次快照的資料,即使其他事務提交了也不會影響當前select的資料,這就實現了可重複讀了。
快照的生成當在第一次執行select的時候,也就是說假設當A開啟了事務,然後沒有執行任何操作,這時候B insert了一條資料然後commit,這時候A執行 select,那麼返回的資料中就會有B新增的那條資料。之後無論再有其他事務commit都沒有關係,因為快照已經生成了,後面的select都是根據第一次的快照來的。
一致性讀依賴mvcc快照,利用事務id遞增特性,來做讀取資料時歷史版本的選擇;
當前讀規則(更新邏輯)
更新資料時都是先讀後寫的,而這個讀,只能讀當前的值也就是最新的值,也被稱為當前讀。
所以,如果把事務 A 的查詢語句 select * from t where id=1 修改一下,加上 lock in share mode 或 for update,也都可以讀到版本號是 101 的資料,返回的 k 的值是 3。下面這兩個 select 語句,就是分別加了讀鎖(S 鎖,共享鎖)和寫鎖(X 鎖,排他鎖)。
mysql> select k from t where id=1 lock in share mode;
mysql> select k from t where id=1 for update;
更新行使用衝突情況
如過一個事務A要更新一行的情況下,該行已經被事務B修改過但還未提交,基於當前讀規則事務A不確定事務B是否會提交或者回滾,必須等待事務B釋放這一行。也就是說事務B的更新過程是對行上了鎖的,即常說的行鎖。
評論總結:當前讀實際上是由行鎖來實現的,持有行鎖的更新操作才能進行當前讀,否則更新操作會阻塞
多版本併發控制與樂觀鎖CAS思想比較
樂觀鎖即一般是在資料表中加上一個版本號version欄位,表示資料被修改的次數,當資料被修改時,version值會加1。當執行緒A要更新資料值時,在讀取資料的同時也會讀取version值,在提交更新時,若剛才讀到的version值與當前資料庫中的version值相等時才更新,否則重試更新操作,直到更新成功。
不同的儲存引擎的MVCC實現方式不同,典型的有樂觀和悲觀兩種思想的實現形式。所有認為“併發事務不算大”而採用非加鎖的形式來實現“加鎖”效果的控制機制我們都認為它是樂觀鎖。
- 多版本併發控制(MVCC)是一種用來解決讀-寫衝突的無鎖併發控制
- 樂觀併發控制(OCC)是一種用來解決寫-寫衝突的無鎖併發控制
參考:樂觀鎖和 MVCC 的區別? - 用心閣的回答 - 知乎 https://www.zhihu.com/question/27876575/answer/71836010
總結
InnoDB 的行資料有多個版本,每個資料版本有自己的 row trx_id,每個事務或者語句有自己的一致性檢視。普通查詢語句是一致性讀,一致性讀會根據 row trx_id 和一致性檢視確定資料版本的可見性。
而讀提交的邏輯和可重複讀的邏輯類似,它們最主要的區別是:
-
在可重複讀隔離級別下,只需要在事務開始的時候建立一致性檢視,之後事務裡的其他查詢都共用這個一致性檢視;
-
對於可重複讀,查詢只承認在事務啟動前就已經提交完成的資料;
-
在讀提交隔離級別下,每一個語句執行前都會重新算出一個新的檢視。
-
對於讀提交,查詢只承認在語句啟動前就已經提交完成的資料;
日誌系統
redo log(物理日誌)
redo log時InnoDB引擎特有的物理日誌。記錄最近的物理資料做了什麼改動
這個寫日誌的過程其實就是 MySQL 裡經常說到的 WAL 技術,WAL 的全稱是 Write-Ahead Logging,它的關鍵點就是先寫日誌,再寫磁碟。具體來說,當有一條記錄需要更新的時候,InnoDB 引擎就會先把記錄寫到 redo log裡面,並更新記憶體中的資料,這個時候更新就算完成了。同時,InnoDB 引擎會在適當的時候,將這個操作記錄更新到磁盤裡面。redo log檔案如果寫滿了就迴圈覆蓋掉之前的繼續寫入。
有了 redo log,InnoDB 就可以保證即使資料庫發生異常重啟,之前提交的記錄都不會丟失,這個能力稱為 crash-safe。
redo log 用於保證 crash-safe 能力。innodb_flush_log_at_trx_commit 這個引數設定成 1 的時候,表示每次事務的 redo log 都直接持久化到磁碟。這個引數我建議你設定成 1,這樣可以保證 MySQL 異常重啟之後資料不丟失。
主要作用:解決資料庫宕機重啟丟失資料的問題!
binlog(邏輯日誌)
binlog(歸檔日誌)是MySQL的Server層的邏輯日誌。Binlog有兩種模式,statement 格式的話是記sql語句, row格式會記錄行的內容,記兩條,更新前和更新後都有。
binlog 會記錄所有的邏輯操作,並且是採用“追加寫”的形式。
主要作用:主要用來做複製、資料備份等操作!
這兩種日誌有以下三點不同:
- redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 層實現的,所有引擎都可以使用。
- redo log 是物理日誌,記錄的是“在某個資料頁上做了什麼修改”;binlog 是邏輯日誌,記錄的是這個語句的原始邏輯,比如“給 ID=2 這一行的 c 欄位加 1 ”。
- redo log 是迴圈寫的,空間固定會用完;binlog 是可以追加寫入的。“追加寫”是指 binlog 檔案寫到一定大小後會切換到下一個,並不會覆蓋以前的日誌。
摘自評論:SQL是面向使用者的語義化命令,你可以理解為高階程式語言。高階程式語言最終會被執行去完成磁碟上資料的操作。我理解redo log記錄的是磁碟上資料的物理變化,binlog記錄的是當時所執行的高階程式語言。物理日誌與儲存引擎相關,邏輯日誌可以跨儲存引擎。
一條update語句執行的過程
總結:InnoDB redo log 寫盤,InnoDB 事務進⼊ prepare 狀態。 如果前⾯ prepare 成功,binlog 寫盤,再繼續將事務⽇志持久化到 binlog,如果持久化成功,那麼 InnoDB 事務則進⼊ commit 狀態(在 redo log ⾥⾯寫⼀個 commit 記錄)
undo log(回滾日誌)
undo log有兩個作用:提供回滾和多個行版本控制(MVCC)。
undo log鏈。結合MVCC章節理解。
參考:
https://database.51cto.com/art/202101/641019.htm
https://www.jianshu.com/p/336e4995b9b8
Innodb邏輯存結構:
innoDB 的資料儲存在表空間中,表空間又包含各種段,其中有資料段,索引段,回滾段。InnoDB中資料以B+Tree的資料結構儲存的,非葉子節點既是索引,葉子節點既是資料行,回滾段用於儲存undoLog,undoLog中記錄的就是多版本資料,用於快照讀和事務失敗後的資料回滾,MySQL在合適的時機會清理undoLog。
————————————————
版權宣告:本文為CSDN博主「房姐」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。
原文連結:https://blog.csdn.net/weixin_29531897/article/details/113432180
併發控制之鎖
表鎖
如何安全地給小表加欄位?
行鎖
Mysql鎖有哪些,如何理解?
按鎖粒度分類:
-
⾏鎖:鎖某⾏資料,鎖粒度最⼩,併發度⾼
-
表鎖:鎖整張表,鎖粒度最⼤,併發度低
-
全域性鎖:鎖的是整個資料庫例項
還可以分為:
-
共享鎖:也就是讀鎖,⼀個事務給某⾏資料加了讀鎖,其他事務也可以讀,但是不能寫
-
排它鎖:也就是寫鎖,⼀個事務給某⾏資料加了寫鎖,其他事務不能讀,也不能寫
還可以分為:
-
樂觀鎖:並不會真正的去鎖某⾏記錄,⽽是通過⼀個版本號來實現的
-
悲觀鎖:上⾯所的⾏鎖、表鎖等都是悲觀鎖
在事務的隔離級別實現中,就需要利⽤鎖來解決幻讀
顯式加鎖:
上共享鎖(讀鎖)的寫法:lock in share mode,例如:
select` `math ``from` `zje ``where` `math>60 lock in share mode;
上排它鎖(寫鎖)的寫法:for update,例如:
select `math `from zje wheremath >60 for update;
參考:https://www.jb51.net/article/193520.htm
表鎖
即鎖住整張表,此時其它事務無法對當前表進行更新或插入操作。
如果沒有索引,update會鎖表,如果加了索引,就會鎖行
拓展:間隙鎖
當我們用範圍條件而不是相等條件檢索資料,並請求共享或排他鎖時,InnoDB會給符合條件的已有資料記錄的索引項加鎖;對於鍵值在條件範圍內並不存在的記錄,叫做間隙
-- 使用者A
update user set count=8 where id>2 and id<6
-- 使用者B
update user set count=10 where id=5;
如果使用者A在進行了上述操作後,事務還未提交,則B無法對2~6之間的記錄進行更新或插入記錄,會阻塞,當A將事務提交後,B的更新操作會執行。
行鎖
顧名思義,行鎖就是針對資料表中行記錄的鎖。這很好理解,比如事務 A 更新了一行,而這時候事務 B 也要更新同一行,則必須等事務 A 的操作完成後才能進行更新。即寫寫衝突。
MySQL的行鎖是通過索引載入的,也就是說,行鎖是加在索引響應的行上的,要是對應的SQL語句沒有走索引,則會全表掃描,行鎖則無法實現,取而代之的是表鎖,此時其它事務無法對當前表進行更新或插入操作。
兩階段鎖
在 InnoDB 事務中,行鎖是在需要的時候才加上的,但並不是不需要了就立刻釋放,而是要等到事務結束時才釋放。這個就是兩階段鎖協議。
知道了這個設定,對我們使用事務有什麼幫助呢?那就是,如果你的事務中需要鎖多個行,要把最可能造成鎖衝突、最可能影響併發度的鎖儘量往後放。
sql操作時什麼時候鎖表,什麼時候鎖行?
如果沒有索引,update會鎖表,如果加了索引,就會鎖行
索引
索引基礎知識
建立索引
修改表方式
-- ALTER TABLE `admin` ADD UNIQUE INDEX `nameIndex` (`name`) USING BTREE ;
ALTER TABLE `admin`
ADD UNIQUE INDEX `loginIndex` (`name`,`PASSWORD`) USING BTREE ;
InnoDB索引
MySQL資料表使用InnoDB作為儲存引擎的時候,資料結構就是使用B+樹,而表的所有資料儲存在主鍵索引上,也就是通常所說的聚簇索引,也就是每個表都需要有個聚簇索引樹。對於InnoDB,主鍵對應的索引就是聚簇索引,表的所有資料都儲存在聚簇索引上。
索引是什麼?
索引就是一個數據結構,我們把表中的記錄用一個適合高效查詢的資料結構來表示,目的就是讓查詢變得更高效。
B+樹演變由來:
二叉查詢樹(缺點:當插入有序時,會生成單支樹的清空)——》平衡二叉樹(規定了左右子樹的高度差不能超過1,如果插入資料導致高度差超過了1則自動進行調整,回覆到平衡狀態)——》B樹(每個結點儲存索引值(主鍵),資料和指標,樹變得矮壯)——》(繼續優化)——》B+樹(非葉子結點儲存索引值,指標)
B+樹相對於B樹有幾點不同:
- 非葉子節點只儲存索引值和指標。
- 所有葉子節點之間都有一個鏈指標(雙向指標)。
- 資料記錄都存放在葉子節點中。
- 優勢:
- 單一節點儲存更多的元素,使得查詢的I/O次數更少。由於非葉子結點不存資料,意味著同樣的大小的磁碟頁可以容納更多指標,使整顆樹變得更加矮壯,便可減少磁碟I/O操作,因為讀取每個結點(磁碟塊)的資料會執行記憶體與磁碟塊的I/O操作;
- 所有葉子節點形成有序連結串列,便於範圍查詢。在範圍查詢方面,B+樹的優勢更加明顯。B樹的範圍查詢需要不斷依賴中序遍歷。首先二分查詢到範圍下限,在不斷通過中序遍歷,知道查詢到範圍的上限即可。整個過程比較耗時。而B+樹的範圍查詢則簡單了許多。首先通過二分查詢,找到範圍下限,然後同過葉子結點的連結串列順序遍歷,直至找到上限即可,整個過程簡單許多,效率也比較高。
- 所有查詢都要查詢到葉子節點,查詢效能穩定。B樹的查詢只需找到匹配元素即可,最好情況下查詢到根節點,最壞情況下查詢到葉子結點,所說效能很不穩定。而B+樹每次必須查詢到葉子結點,效能穩定。
參考連結:https://www.zhihu.com/question/26113830/answer/908074473
資料庫,併發方面,執行緒池,分散式,多執行緒
聚簇索引和非聚簇索引
MySQL資料庫的索引分為聚集索引和非聚集索引
主鍵都會自動生成聚簇索引
所有不是聚簇索引的索引都叫非聚簇索引或者輔助索引。
在InnDB儲存引擎中,每個輔助索引的每條記錄都包含主鍵,也包含非聚簇索引指定的列。
MySQL使用這個主鍵值來檢索聚簇索引。
因此應該儘可能將主鍵縮短,否則輔助索引佔用空間會更大。
一般來說用自增的整數型列作為主鍵列。
特徵與區別
innoDb儲存引擎中的聚簇索引表中的資料按主鍵的順序存放,它實際上就是按主鍵構建的一個B+樹,葉子節點存放的是資料行記錄。聚簇索引只可能是主鍵,或者是組成唯一鍵中的所有列都為NOT NULL的第一個唯一索引,或者隱式建立的聚簇索引這三種情況(重點:唯一,非空)
所以資料庫中的資料實際上是索引的一部分。由於實際的資料頁只能按照一個順序存放,所以每張表聚簇索引只能有一個。
非聚簇索引的葉子節點中存放的是鍵值和主鍵值,所以通過非聚簇索引需要先查詢到主鍵值然後通過聚簇索引查詢到具體的資料。非聚集索引並不會影響到資料的儲存順序,所以非聚集索引可以存在多個。
字元的ASCII碼作為比較準則。聚集索引這種實現方式使得按主鍵的搜尋十分高效,但是輔助索引搜尋需要檢索兩遍索引:首先檢索輔助索引獲得主鍵,然後用主鍵到主索引中檢索獲得記錄。
我們每向表中插入一條記錄,本質上就是向該表的聚簇索引以及所有二級索引代表的B+樹的節點中插入資料。而B+樹的每一層中的頁都會形成一個雙向連結串列,如果是以頁為單位來分配儲存空間的話,雙向連結串列相鄰的兩個頁之間的物理位置可能離得非常遠
優化建議
不同儲存引擎的索引實現方式對於正確使用和優化索引都非常有幫助,例如知道了InnoDB的索引實現後,就很容易明白:
- 為什麼不建議使用過長的欄位作為主鍵,因為所有輔助索引都引用主索引,過長的主索引會令輔助索引變得過大。
- 再例如,用非單調的欄位作為主鍵在InnoDB中不是個好主意,因為InnoDB資料檔案本身是一顆B+Tree,非單調的主鍵會造成在插入新記錄時資料檔案為了維持B+Tree的特性而頻繁的分裂調整,十分低效,而使用自增欄位作為主鍵則是一個很好的選擇。
InnoDB 表是基於聚簇索引建立的。因此InnoDB 的索引能提供一種非常快速的主鍵查詢效能。不過,它的輔助索引(Secondary Index, 也就是非主鍵索引)也會包含主鍵列,所以,如果主鍵定義的比較大,其他索引也將很大。如果想在表上定義 、很多索引,則爭取儘量把主鍵定義得小一些。InnoDB 不會壓縮索引。
InnoDB使用的是聚簇索引,將主鍵組織到一棵B+樹中,而行資料就儲存在葉子節點上,若使用"where id = 14"這樣的條件查詢主鍵,則按照B+樹的檢索演算法即可查詢到對應的葉節點,之後獲得行資料。若對Name列進行條件搜尋,則需要兩個步驟:第一步在輔助索引B+樹中檢索Name,到達其葉子節點獲取對應的主鍵。第二步使用主鍵在主索引B+樹中再執行一次B+樹檢索操作,最終到達葉子節點即可獲取整行資料。
參考連結:
MySQL聚簇索引和非聚簇索引的理解_明明如月的技術部落格-CSDN部落格_mysql聚簇索引和非聚簇索引
MySQL InnoDB資料表缺少主鍵會怎樣 - 知乎 (zhihu.com)
聯合索引
最左字首原則
比如聯合索引(a,b,c)的時候可以支援a、(a,b)、(a,b,c) 3種組合進行查詢。
b+數是按照從左到右的順序來建立搜尋樹的,比如當(a=? and b=? and c=?)這樣的資料來檢索的時候,b+樹會優先比較a列來確定下一步的所搜方向,如果a列相同再依次比較b列和c列,最後得到檢索的資料。
又比如當(a=? and c=?)這樣的資料來檢索時,b+樹可以用a列來指定搜尋方向,但下一個欄位b列的缺失,所以只能把a列的資料找到,然後再匹配c列的資料了,便只能使用索引的第一列了, 這個是非常重要的性質,即索引的最左匹配特性。
覆蓋索引
回表概念:當我們需要查詢某個欄位,通過條件走普通索引查詢定位到主鍵後,需要回到主鍵索引樹搜尋的過程,我們稱為回表。
但是是否可以避免回表通過一次索引查詢就得到值呢,答案是使用覆蓋索引。如執行的語句是是:
select ID from T where k between 3 and 5
ID是主鍵,普通索引k的樹都會帶上主鍵索引的值,即該ID列是在k這個索引樹上的。這就不需要回表。由於覆蓋索引可以減少樹的搜尋次數,顯著提升查詢效能,所以使用覆蓋索引是一個常用的效能優化手段。
阿里巴巴手冊:
【推薦】利用覆蓋索引來進行查詢操作,避免回表。
說明:如果一本書需要知道第 11 章是什麼標題,會翻開第 11 章對應的那一頁嗎?目錄瀏覽一下就好,這個目錄就是起到覆蓋索引的作用。
正例:能夠建立索引的種類分為主鍵索引、唯一索引、普通索引三種,而覆蓋索引只是一種查詢的一種效果,用 explain 的結果,extra 列會出現:using index。
如何實現覆蓋索引?
常見的方法是:將被查詢的欄位,建立到聯合索引裡去。
例如:select id,age,name from user where age = 10;
使用索引覆蓋:建組合索引idx_age_name(age,name)即可。
關於explain命令使用與內容參考:https://www.jb51.net/article/180267.htm
總結如下:
- 聯合索引的使用在寫where條件的順序無關,mysql查詢分析會進行優化而使用索引。但是減輕查詢分析器的壓力,最好和索引的從左到右的順序一致。
- 使用等值查詢,多列同時查詢,索引會一直傳遞並生效。因此等值查詢效率最好。
- 索引查詢遵循最左側原則。但是遇到範圍查詢列(>、<、between、like)之後的列索引失效只能回表。但5.6之後引入的索引下推優化,可以在索引遍歷過程中,對索引中包含的欄位先做判斷,直接過濾掉不滿足條件的記錄,減少回表次數。
- 排序也能使用索引,合理使用索引排序,避免出現file sort。
索引失效問題
- 模糊查詢:like關鍵字以%開頭
- 如果索引了多列,要遵守最左字首法則(查詢按順序從索引的最左前列開始,並且不跳過索引中的列)。
- 對索引的欄位使用內部函式,索引會失效(應該建立基於函式的索引)
- 在索引列上進行運算操作,索引將失效。
- 索引列儘量不要儲存null值(在mysql中,含有空值的列很難進行查詢優化。因為它們使得索引、索引的統計資訊以及比較運算更加複雜。你應該用0、一個特殊的值或者一個空串代替空值。)
- 字串匹配資料沒有加單引號
- 範圍查詢右邊的欄位索引會失效
- 匹配欄位資料型別錯誤
MySQL資料型別相關問題
mysql中int、bigint、smallint 和 tinyint的區別詳細介紹
tinyint佔一個位元組,int佔四個位元組
然後tinyint(1) 和 tinyint(4) 中的1和4並不表示儲存長度,只有欄位指定zerofill是有用,無符號和zerofill的時候會填充0,顯示成M對應的寬度。如tinyint(4),如果實際值是2,如果列指定了zerofill,查詢結果就是0002,左邊用0來填充。
MySQL 中的 varchar 和 char 有什麼區別
char 是一個定長欄位,假如申請了 char(10)的空間,那麼無論實際儲存多少內容.該欄位都佔用 10 個字元。
而 varchar 是變長的,也就是說申請的只是最大長度,佔用的空間為實際字元長度+1,最後一個字元儲存使用了多長的空間.
char(M)型別的資料列裡,每個值都佔用M個位元組,如果某個長度小於M,MySQL就會在它的右邊用空格字元補足。(在檢索操作中那些填補出來的空格字元將被去掉)
基本理論知識與操作命令
資料庫語言
DDL:資料定義語言
DML:資料操縱語言
DCL:資料控制語言
MySQL登陸步驟
1.啟動管理員模式下的CMD,執行cd /d E:\mysql-8.0.23\bin命令跳轉到mysql目錄下
2.使用net start mysql命令啟動MYSQL服務,net stop mysql命令停止MYSQL服務
3.使用mysql -u root -p命令使用者名稱進行登陸,密碼Enter password:123456
4.命令set password='123456';修改密碼。命令flush privileges;重新整理許可權
顯示已有資料庫:show databases;
切換資料庫:use 資料庫名稱;
顯示該資料庫下的所有表:show tables;
顯示建表語句: show create table 表名;
刪除資料庫:drop database;
將指令碼檔案匯入資料庫中:
1.建立資料庫:create database 資料庫名稱;
2.切換到新建的資料庫:use 資料庫名稱;
3.匯入sql指令碼,如:source D:/Users/Administrator/桌面/EmpDB.sql;
檢視時區:
show variables like'%time_zone';
設定時區:
輸入 set global time_zone = '+8:00'; (注意不要漏掉後面的分號)
linux中mysql服務的常用命令
1.查詢檔案的具體路徑 find / -name 檔名
2.重啟mysql服務 service mysqld restart
3.停止mysql服務 service mysqld stop
4.啟動mysql服務 service mysqld start
5.登入本機mysql資料庫 mysql -u root -p 輸入密碼
6.檢視mysql執行狀態 service mysqld status
7.檢視mysql的執行使用的程序 ps -e |grep mysql
SQL命令筆記
分頁limit
https://www.cnblogs.com/xiaoshen666/p/10824117.html
表連線
簡單連線,左連線,右連線
- INNER JOIN:如果表中有至少一個匹配,則返回行(即相等的行返回)
- LEFT JOIN:即使右表中沒有匹配,也從左表返回所有的行
- RIGHT JOIN:即使左表中沒有匹配,也從右表返回所有的行
SELECT column_name(s)
FROM table1
INNER JOIN table2
ON table1.column_name=table2.column_name;
group by
group by having
TRUNCATE語句和DELETE語句的區別
1、delete語句,是DML語句,truncate語句通常被認為是DDL語句。
2、delete語句,後面可以跟where子句,通常指定where子句中的條件表示式,只刪除滿足條件的部分記錄。而truncate語句,只能用於刪除表中的所有記錄。
3、truncate語句,刪除表中的資料後,向表中新增記錄時,自動增加欄位的預設初始值重新從1開始,而使用delete語句,刪除表中所有記錄後,向表中新增記錄時,自動增加欄位的值,為刪除時該欄位的最大值加1,也就是在原來的基礎上遞增。
4、delete語句,每刪除一條記錄,都會在日誌中記錄,而使用truncate語句,不會在日誌中記錄刪除的內容,因此,truncate語句的執行效率比delete語句高。
————————————————
delete 和 truncate 僅僅刪除表資料,drop 連表資料和表結構一起刪除
原文連結:https://blog.csdn.net/nangeali/article/details/73620044
相關知識
Mysql 中 MyISAM 和 InnoDB 的區別有哪些?
- InnoDB 支援事務,MyISAM 不支援事務。這是 MySQL 將預設儲存引擎從 MyISAM 變成 InnoDB 的重要原因之一;
- InnoDB 是聚集索引,MyISAM 是非聚集索引。
- InnoDB 最小的鎖粒度是行鎖,MyISAM 最小的鎖粒度是表鎖。
- InnoDB 支援外來鍵,而 MyISAM 不支援。
如何選擇:
- 如果表中絕大多數都只是讀查詢,可以考慮 MyISAM,如果既有讀寫也挺頻繁,請使用InnoDB。
參考知乎 https://www.zhihu.com/question/20596402/answer/211492971
mysql-connector-java的常用連線配置引數
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/mytest?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
注:useSSL安全連線、useUnicode指定字元的編碼、解碼格式
解決MySQL8時區問題
方法一:修改java中的時區為東八區
#serverTimezone可以設定為北京時間GMT%2B8、上海時間Asia/Shanghai或者香港時間Hongkong
url: jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=true
方法二:修改MySQL資料庫的時區為東八區
// 一:使用命令(優點:不需要重啟MySQL服務,缺點:一旦MySQL服務被重啟,設定就會消失)
mysql> set time_zone = '+8:00';
mysql> set global time_zone = '+8:00';
// 二:修改my.ini配置檔案(優點:永久儲存設定,缺點:需重啟MySQL服務)
[mysqld]
// 設定預設時區
default-time_zone='+8:00'
原文連結:https://blog.csdn.net/starlemon2016/article/details/90314649
修改mysql root密碼報錯
原修改命令
mysql> update user set password=password(“新密碼”) where user=”使用者名稱”;
執行後報錯 ERROR 1054(42S22) Unknown column 'password' in ‘field list’
錯誤的原因是 5.7版本下的mysql資料庫下已經沒有password這個欄位了,password欄位改成了authentication_string
https://www.cnblogs.com/wangbaobao/p/7087032.html
開啟MySQL遠端訪問許可權 允許遠端連線
實現遠端連線(改表法)
use mysql;
update user set host = '%' where user = 'root';
flush privileges;
這樣在遠端就可以通過root使用者訪問Mysql.
https://blog.csdn.net/chenlongjs/article/details/86502323?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-0.no_search_link&spm=1001.2101.3001.4242
2022/02/22 更
navicate連線報錯:client does not support authentication
mysql 8.0貌似需要以下的寫法才可以解決
alter user 'root'@'%' identified with mysql_native_password by '123456';
資料庫id自增問題
自增是獨立於資料表存在的,你可以理解為它是一個單獨的序列函式,每次insert的時候去這個函式取一下當前的值。該函式只增不減,永遠加1。它只會基於最近一次的序列值上自增(也就是加1)。
所以,如果你刪除了資料表中的一行記錄,那麼這個id就再也不存在了。
資料庫中的分散式ID
全域性唯一ID
參考:https://zhuanlan.zhihu.com/p/107939861
雪花演算法概述
雪花演算法生成的ID是純數字且具有時間順序的
一、特點(自增、有序、適合分散式場景)
- 時間位:可以根據時間進行排序,有助於提高查詢速度。
- 機器id位:適用於分散式環境下對多節點的各個節點進行標識,可以具體根據節點數和部署情況設計劃分機器位10位長度,如劃分5位表示程序位等。
- 序列號位:是一系列的自增id,可以支援同一節點同一毫秒生成多個ID序號,12位的計數序列號支援每個節點每毫秒產生4096個ID序號
snowflake演算法可以根據專案情況以及自身需要進行一定的修改。
二、總結
分散式唯一ID的方案有很多,雪花演算法的組成結構大致分為了無效位、時間位、機器位和序列號位。其特點是自增、有序、純數字組成查詢效率高且不依賴於資料庫。適合在分散式的場景中應用,可根據需求調整具體實現細節。
參考:https://developer.51cto.com/art/201909/602525.htm
資料庫設計正規化:
第一正規化:有主鍵,每一個欄位都是不可分割的最小單元
第二正規化:滿足第一正規化,除主鍵外的所有列都必須完全依賴於主鍵,而不應該部分依賴;非主屬性完全依賴於主屬性.(解決方式:拆分成兩個表,並增加關係表,關係表中有兩個表的外來鍵
第三正規化:(解決方式:拆分成兩個表,消除傳遞依賴)