MySQL數據庫基礎知識及操作


MySQL數據庫基礎知識及操作

MySQL服務器邏輯架構圖

MySql服務器邏輯架構圖

問:innodb引擎執行一條select語句的過程?

連接器

第一步,你會先連接到這個數據庫上,這時候接待你的就是連接器。連接器負責跟客戶端建立連接、獲取權限、維持和管理連接。連接命令:

mysql -h$ip -P$port -u$user -p

連接命令中的 mysql 是客戶端工具,用來跟服務端建立連接。在完成經典的 TCP 握手后,連接器就要開始認證你的身份,這個時候用的就是你輸入的用戶名和密碼。

  • 如果用戶名或密碼不對,你就會收到一個"Access denied for user"的錯誤,然后客戶端程序結束執行。
  • 如果用戶名密碼認證通過,連接器會到權限表里面查出你擁有的權限。之后,這個連接里面的權限判斷邏輯,都將依賴於此時讀到的權限。

這就意味着,一個用戶成功建立連接后,即使你用管理員賬號對這個用戶的權限做了修改,也不會影響已經存在連接的權限。修改完成后,只有再新建的連接才會使用新的權限設置。

長連接和短連接

數據庫里面,長連接是指連接成功后,如果客戶端持續有請求,則一直使用同一個連接。短連接則是指每次執行完很少的幾次查詢就斷開連接,下次查詢再重新建立一個。

建立連接的過程通常是比較復雜的,所以我建議你在使用中要盡量減少建立連接的動作,也就是盡量使用長連接。

mysql執行過程中一些數據綁定在連接對象中,因此全部都用長連接長時間不斷開會導致內存占用太多。所以如果長連接累積下來,可能導致內存占用太大,被系統強行殺掉(OOM),從現象看就是 MySQL 異常重啟了。

怎么解決這個問題呢?你可以考慮以下兩種方案。

  1. 定期斷開長連接。使用一段時間,或者程序里面判斷執行過一個占用內存的大查詢后,斷開連接,之后要查詢再重連。
  2. 如果你用的是 MySQL 5.7 或更新版本,可以在每次執行一個比較大的操作后,通過執行 mysql_reset_connection 來重新初始化連接資源。這個過程不需要重連和重新做權限驗證,但是會將連接恢復到剛剛創建完時的狀態。

查詢緩存

但是大多數情況下我會建議你不要使用查詢緩存,為什么呢?因為查詢緩存往往弊大於利。查詢緩存的失效非常頻繁,只要有對一個表的更新,這個表上所有的查詢緩存都會被清空。因此很可能你費勁地把結果存起來,還沒使用呢,就被一個更新全清空了。對於更新壓力大的數據庫來說,查詢緩存的命中率會非常低。除非你的業務就是有一張靜態表,很長時間才會更新一次。比如,一個系統配置表,那這張表上的查詢才適合使用查詢緩存。

需要注意的是,MySQL 8.0 版本直接將查詢緩存的整塊功能刪掉了,也就是說 8.0 開始徹底沒有這個功能了。

分析器

如果沒有命中查詢緩存,就要開始真正執行語句了。首先,MySQL 需要知道你要做什么,因此需要對 SQL 語句做解析。分析器先會做“詞法分析”。你輸入的是由多個字符串和空格組成的一條 SQL 語句,MySQL 需要識別出里面的字符串分別是什么,代表什么。

優化器

經過了分析器,MySQL 就知道你要做什么了。在開始執行之前,還要先經過優化器的處理。優化器是在表里面有多個索引的時候,決定使用哪個索引;或者在一個語句有多表關聯(join)的時候,決定各個表的連接順序。

執行器

執行器MySQL 通過分析器知道了你要做什么,通過優化器知道了該怎么做,於是就進入了執行器階段,開始執行語句。

MySQL 整體來看,其實就有兩塊:一塊是 Server 層,它主要做的是 MySQL 功能層面的事情;還有一塊是引擎層,負責存儲相關的具體事宜

img

存儲引擎

常見的有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。如圖所示,就是一個記錄被多個事務連續更新后的狀態。

img

圖中虛線框里是同一行數據的 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 會記錄所有的邏輯操作,並且是采用“追加寫”的形式。

主要作用:主要用來做復制、數據備份等操作!

這兩種日志有以下三點不同:

  1. redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 層實現的,所有引擎都可以使用。
  2. redo log 是物理日志,記錄的是“在某個數據頁上做了什么修改”;binlog 是邏輯日志,記錄的是這個語句的原始邏輯,比如“給 ID=2 這一行的 c 字段加 1 ”。
  3. redo log 是循環寫的,空間固定會用完;binlog 是可以追加寫入的。“追加寫”是指 binlog 文件寫到一定大小后會切換到下一個,並不會覆蓋以前的日志。

摘自評論:SQL是面向用戶的語義化命令,你可以理解為高級編程語言。高級編程語言最終會被執行去完成磁盤上數據的操作。我理解redo log記錄的是磁盤上數據的物理變化,binlog記錄的是當時所執行的高級編程語言。物理日志與存儲引擎相關,邏輯日志可以跨存儲引擎。

一條update語句執行的過程

image-20211021211136950

img

總結: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鎖有哪些,如何理解?

按鎖粒度分類:

  1. ⾏鎖:鎖某⾏數據,鎖粒度最⼩,並發度⾼

  2. 表鎖:鎖整張表,鎖粒度最⼤,並發度低

  3. 全局鎖:鎖的是整個數據庫實例

還可以分為:

  1. 共享鎖:也就是讀鎖,⼀個事務給某⾏數據加了讀鎖,其他事務也可以讀,但是不能寫

  2. 排它鎖:也就是寫鎖,⼀個事務給某⾏數據加了寫鎖,其他事務不能讀,也不能寫

還可以分為:

  1. 樂觀鎖:並不會真正的去鎖某⾏記錄,⽽是通過⼀個版本號來實現的

  2. 悲觀鎖:上⾯所的⾏鎖、表鎖等都是悲觀鎖

在事務的隔離級別實現中,就需要利⽤鎖來解決幻讀

顯式加鎖:

上共享鎖(讀鎖)的寫法: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會鎖表,如果加了索引,就會鎖行

索引

索引基礎知識

https://aflyun.blog.csdn.net/article/details/81102957?spm=1001.2101.3001.6650.4&utm_medium=distribute.wap_relevant.none-task-blog-2~default~CTRLIST~default-4.wap_blog_relevant_pic&depth_1-utm_source=distribute.wap_relevant.none-task-blog-2~default~CTRLIST~default-4.wap_blog_relevant_pic

建立索引

修改表方式

-- 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+樹(非葉子結點存儲索引值,指針)

img

B+樹相對於B樹有幾點不同:

  1. 非葉子節點只存儲索引值和指針。
  2. 所有葉子節點之間都有一個鏈指針(雙向指針)。
  3. 數據記錄都存放在葉子節點中。
  4. 優勢:
    • 單一節點存儲更多的元素,使得查詢的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。

索引失效問題

  1. 模糊查詢:like關鍵字以%開頭
  2. 如果索引了多列,要遵守最左前綴法則(查詢按順序從索引的最左前列開始,並且不跳過索引中的列)。
  3. 對索引的字段使用內部函數,索引會失效(應該建立基於函數的索引)
  4. 在索引列上進行運算操作,索引將失效。
  5. 索引列盡量不要存儲null值(在mysql中,含有空值的列很難進行查詢優化。因為它們使得索引、索引的統計信息以及比較運算更加復雜。你應該用0、一個特殊的值或者一個空串代替空值。
  6. 字符串匹配數據沒有加單引號
  7. 范圍查詢右邊的字段索引會失效
  8. 匹配字段數據類型錯誤

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:數據控制語言

img

mysql windows平台安裝

5.7版本安裝
https://blog.csdn.net/weixin_43395911/article/details/99702121

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 的區別有哪些?

  1. InnoDB 支持事務,MyISAM 不支持事務。這是 MySQL 將默認存儲引擎從 MyISAM 變成 InnoDB 的重要原因之一;
  2. InnoDB 是聚集索引,MyISAM 是非聚集索引。
  3. InnoDB 最小的鎖粒度是行鎖,MyISAM 最小的鎖粒度是表鎖。
  4. InnoDB 支持外鍵,而 MyISAM 不支持。

如何選擇:

  1. 如果表中絕大多數都只是讀查詢,可以考慮 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

數據庫設計范式:

第一范式:有主鍵,每一個字段都是不可分割的最小單元

第二范式:滿足第一范式,除主鍵外的所有列都必須完全依賴於主鍵,而不應該部分依賴;非主屬性完全依賴於主屬性.(解決方式:拆分成兩個表,並增加關系表,關系表中有兩個表的外鍵

第三范式:(解決方式:拆分成兩個表,消除傳遞依賴)

image-20210421211031200


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM