日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

mysql 自身参照自身_MySQL入门

發布時間:2024/7/23 数据库 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mysql 自身参照自身_MySQL入门 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1.SQL查詢操作

select的“另類”用法

我們通常習慣select + from從數據表中讀取數據,不過實際上select并不一定要去讀取數據庫中的內容。

比如:

select 1+1; 返回2

select now(); 返回當前時間

select本身代表要返回的內容,至于與數據庫表中存的數據是否有關并不重要。

同時MySQL支持基本四則運算,所以可以利用這兩個特性來實現:

統計A隊列的案件數量比B隊列的案件數量差異:

select

(select count(id) from assignment where queue='A' and `status`='ACTIVE' and active=1)

-

(select count(id) from assignment where queue='B' and `status`='ACTIVE' and active=1);

where和having

where和having都有對數據進行篩選的功能,通常的使用習慣是where跟在from后面,而having跟在group by后面,那么是不是非得這么用呢?二者的區別在哪里呢?

where表示基于數據表的列參數的限制條件,即where a=b語句中的a必須是數據表中的列。

having表示基于select語句返回的變量的限制條件,即having a=b語句中的a必須在select中出現。

來看下面幾種場景:

select * from table where a=XXX;

select * from table having a=XXX;

// 二者均可使用,效果一樣

select a,b,c from table where d=xxx; // ok

select a,b,c from table having d=xxx; // 錯誤,d不在select選擇之中,無法用來having

select a, count(b) from table where count(b)>100; // 錯誤,where只能跟表中存在的列,無法對聚合、函數操作后的變量進行篩選

select a, count(b) from table group by a having count(b)>100; // ok

distinct和group by

distinct()函數和group by 語句都有用來去重的效果,但對應的使用場景有些差異。

distinct的作用就是單純的去重,必須緊跟著select的后面,否則會報錯。當select中涉及的到所有元素都重復時,只返回1條記錄。有時和count連用,統計不重復的數量。

select distinct(a) from table; // 返回1,2,3,5,6,7,9...

select distinct(a),b from table; // 返回 (1,2),(1,3),(2,1),(3,5),(3,6)....

select a,distinct(b) from table; // error

select count(distinct(a)) from table; // 統計a不重復的記錄數量

group by的作用是做分組匯總統計,必須配合聚合函數(count、sum、avg等)使用,否則無意義。返回結果中,被group by的參數每個只有1行。

select avg(b) from table group by a //對每一個a的條件查詢b的平均值

select count(distinct(c)) from table group by a //對每個a的分組統計不同的c列值的數量

關聯查詢

當from語句選擇了2張或以上的表時,返回內容是兩張表的笛卡爾積,總數是二者數據量的乘積!

因此為了避免數據量的爆炸,關聯查詢多表時必須要加聯結條件。

基本句式是:

select a ·聯結方式· b on ·聯結條件·

聯結方式有內聯結、外聯結、左聯結、右聯結等,詳見下圖:

關聯查詢

修改操作涉及自身查詢

有時候在做update、delete操作時需要結合select子句確定修改的范圍。當select子句查詢涉及要修改的表格本身時,要特別注意:不能select出數據,再在同一個表中做update、delete操作。

來看一個例子:a表中現在出現了b列同一個值有多條ACTIVE的記錄的錯誤情況,現在要將這些記錄全部設為EXPIRE。

下面是錯誤的示范:

UPDATE a SET `status` = 'EXPIRE'

WHERE b IN (

SELECT b FROM a

WHERE `status` = 'ACTIVE'

GROUP BY b

HAVING count(*) > 1

);

執行報錯: You can't specify target table 'a' for update in FROM clause

要正常實現該功能,需要在update和select操作直接加一層臨時表,臨時表里存放從原表select出來的數據,update原表時,子查詢讀取臨時表的數據,從而避免報錯。

正確寫法:

UPDATE a SET `status` = 'EXPIRE'

WHERE b IN (

SELECT temp.b from (

SELECT b from a

WHERE `status` = 'ACTIVE'

GROUP BY b

HAVING count(*) > 1

) as temp

);

這里從a表中查詢出來的b全部放進了temp表中,update的時候IN語句讀取temp表中的值,避免了兩個操作指向同一張表產生報錯。

2.InnoDB引擎和索引的技巧

InnoDB引擎的b+樹索引

如果留心過數據庫表信息的話肯定能發現,我們用的數據庫默認都是InnoDB引擎的。MySQL建立數據庫表時,除非顯示聲明,否則建立的都是InnoDB引擎。InnoDB的優勢在于事務安全性和更細粒度的行級鎖,詳見下一章。

InnoDB引擎的索引機制是b+樹,簡稱b樹索引。至于什么是b+樹,請自行百度。

.

.

.

算了,還是講下吧。。。。

查詢一般有三類:順序查詢(慢)、二分查詢(快)和hash查詢(最快,但是費空間,除了內存機制的應用,一般不會使用)。順序查詢和二分查詢有著數量級的性能差距,對于數據量大的情況差異尤其明顯。

為了更快的定位查詢到需要的數據,就要想辦法把慢的順序查詢變為快的二分查詢,這就是索引的本質。

InnoDB中的索引分為主鍵索引(聚簇索引)和二級索引,其中主鍵索引和其他數據是存放在一起的,二級索引有單獨的索引樹。

先來看主鍵索引。

為了實現二分查詢,MySQL規定,主鍵索引必須唯一并按照升序排列(否則沒法二分)。

數據存儲的的基本單位是“頁”,一頁大小為16k,為連續的存儲空間。

連續就好辦了,由于規定了主鍵唯一升序,要在一頁內查找某個主鍵,就可以先讀取中間地址(更準確的說法是槽位)存儲的數據,拿主鍵來比較,如果比中間的主鍵值大,那在后半空間里再去一半位置地址的數據比較,以此類推。

當一頁存不下的時候,就需要開一個新的頁,但頁和頁之間的地址未必是連續的,二者通過雙向鏈表關聯。所以當數據量大,不止一頁的時候如何實現二分呢?

答案是建立一個目錄頁,目錄頁和存儲數據的頁結構上其實是一樣一樣的,存儲的內容是主鍵和其對應的頁的地址。這樣查詢的過程變成:先在目錄頁中二分查找主鍵,找到對應數據頁的地址,進入數據頁再次進行二分查找。

若數據量很大,目錄頁也不止一個怎么辦?那就再建立一個指向目錄頁的二級目錄頁。

整個過程就像是根據頁碼查目錄,先搜索章節、再搜索小節、再搜索小小節。

詳見下圖:

比如我要搜id=26的數據,首先<62找到目錄頁17,然后<50找到數據頁37,在頁37中最后二分法找到26的記錄。

主鍵索引樹

這個形狀不就是樹結構嗎?來看一下這個樹有什么特點:

首先,所有的具體數據都存在葉子節點

非葉子節點只存儲Key值和通往下一級節點的地址

我們管具有這種特點的樹叫做b+樹。InnoDB引擎通過b+樹實現索引的二分查詢,從而提升查詢性能。

那手動建立的其他索引呢?

答案是,每建立一個索引,就建一顆對應的b+樹,并按照該索引字段升序排列(MySQL不止是數值可以排序,任何類型的值都可以,比如字符串是根據字母排列順序來排序)。

在按索引查詢時,就在對應的索引樹上進行二分查詢操作,和主鍵樹一樣。

除主鍵外的所有索引都是二級索引,其b+樹和主鍵b+樹唯一的區別在于葉子節點:主鍵樹的葉子節點存放主鍵和具體記錄的數據;二級索引樹的葉子節點存放索引和主鍵。

還有一種比較特別的二級索引,里面包括多個列的值,稱為聯合索引。一個聯合索引也是一個b+數,內部的順序先按照建立索引的第一個字段排序,相同情況下再按照第二個字段排序,以此類推。

二級索引是怎么查詢數據的呢?首先通過二級索引樹二分查詢找到對應的主鍵id,再拿這些主鍵id去主鍵索引樹上查詢對應的數據,這個過程稱為“回表”。

所以,根據主鍵id查數據走一次索引,根據普通索引查數據要走兩次索引。

如何使用索引

索引將順序查詢升級為二分查詢,使得性能可以大大提升,但索引也不是萬能的,要用索引之前首先得知道使用的代價:

每建一個索引都要建一個b+樹,占空間是肯定的了,如果索引的值很大的話,更容易產生分頁現象,b+樹就需要更多的層,占空間就更大;

不能“管殺不管埋”,建了索引樹還得維護,當表內插入、變更、刪除數據時,對應的索引樹可能也需要同時更新,這樣會拖慢數據庫操作的性能;

對于二級索引的使用,可能查出多個主鍵id,而這些主鍵id未必是連續的,所以回表查詢時是隨機I/O,比順序I/O的性能會差很多,可能得不償失;

所以,一個表上建的索引越多,占用空間就越大,增刪改記錄時的時間性能就越差。因此要建立索引,就要保證其能被充分的利用。

網上對于什么情況下可以命中索引,什么情況下不行的列舉較為零碎,不好理解,但事實上只要理解了索引的b+樹結構原理,判斷能否命中索引其實不難。

先來看幾個這輩子也別想命中索引的操作:

select * from table where a!=xxx // !=, not in 這種操作顯然是沒法通過二分查找來快速定位的

select * from table where a*2=xxx // 對索引變量做運算會導致mysql把a*2作為整體來篩選,無法使用b+樹,函數處理同理,所以對于字符串類型的變量保存數字的列要特別小心。

select * from table order by a // 排序本身不是問題,問題在于沒有where子句,是全部數據的排序,走索引只會增加回表操作開銷,不如全表搜索。

select * from table order by a, b desc limit 10 // 如果有多個字段做排序,排序的方式需要一樣,才能方便的從b+樹上正著取或者倒著取數據。

select * from table where b like '%xxx%' // 字符串按照字母順序來排序,前綴做通配會導致無法判斷其大小范圍。

聯合索引(a,b,c)的情況:

select * from table where b=xxx // 聯合索引按建立索引的順序在b+樹上進行升序排列,所以不使用a的情況下直接用b,由于a的值不確定,無法命中

select * from table where a=xxx and c=xxx // 只能命中索引a,原理同上

select * from table where a>xxx and b=xxx // 還是只能命中索引a,因為a進行的是范圍操作,會篩選出多個值,無法在同一顆索引樹上方便的匹配第二個字段,系統會把b字段的匹配變為順序搜索

理解了索引基于b+樹的搜索方式,就比較容易找正確的使用姿勢了:

表本身的數據量要大,否則二分查詢節約的時間不如回表操作費的時間;

索引盡量小,節約b+樹空間和搜索層次數,降低維護成本;

索引的取值盡可能多(唯一就更好了),避免索引樹上查到大量主鍵id,在回表過程中浪費時間;

基于二分查詢的原理,表達式應該是等值匹配、IN匹配、大小判斷或者用于排序、分組操作,如果是字符串匹配,前綴要準確;

索引列要單獨出現,不要做任何算術、函數運算;

若有多個字段會同時使用,可以建立聯合索引,一是減少b+樹的數量節約空間;二是因為一次查詢只會用一個索引樹,聯合索引效率更高;

對于聯合索引,建立時要按照使用頻率順序來建立,避免出現前一個未使用而使用后一個的情況;

排序情況下一定要限制數量或者條件,不用全表排序;

如果可以,只搜索索引字段,避免select *,索引覆蓋的情況下只需要搜索索引樹,免去了回表操作;

對于IN操作,可以拆解成多個等值匹配,用UNION算符并連結果;

等等.....

MySQL自帶的explain是分析查詢性能的好工具。

explain

這里主要看type和key這倆字段。

key表示這個查詢用到的索引。

type表示這個查詢的索引使用效率。type大概有以下幾種:

const 使用主鍵索引匹配,或者唯一索引匹配非null值。精確匹配一條,速度最快。

ref 使用普通二級索引匹配。會匹配出多個主鍵進行回表查詢。

range 使用索引進行范圍查找。

index 使用了不帶索引的字段進行條件篩選。會在索引匹配后順序判斷每一條是否符合條件。

all 無法命中索引,全表掃描。

3.事務&數據庫鎖

事務和MVCC

數據庫的變更操作是個“危險”的事情,尤其是對于需要幾個語句共同合作完成的操作(例如案件的出催),可能受到硬件性故障例如斷網斷電、并發影響,導致執行一半中斷或者中途數據錯亂的問題。

所以我們要確保這樣一個操作場景,要么一次性執行完成不受影響,要么干脆別執行。這種操作系列稱之為事務。

事務具備四個基本特性,稱為ACID:

Atomicity 原子性,該操作不可分割打斷,保障可以一次性執行完成

Consistency 一致性,所有的操作要滿足約束條件(唯一、not null等)

Isolation 隔離性,事務之間不能相互干擾,引起數據錯亂(臟寫)

Durability 持久性, 已經完成的事務所修改的數據不會因任何故障丟失

InnoDB引擎支持事務操作,以begin開始,以commit提交結束。

在高并發情況下,為了性能考慮,事務間的隔離性有時會做出一點取舍。引起數據錯亂的臟寫肯定是要杜絕的,但還有幾種情況有時要求就沒有那么嚴格:

臟讀:事務A讀到了事務B修改過的數據,但此時事務B未提交

不可重復讀:事務A以同樣的查詢語句查詢,而過程中有其他事務修改了數據,引起每次查詢的結果不同

幻讀:事務A以同樣的查詢語句查詢,而過程中有其他事務插入了滿足查詢條件的新數據,導致A查出了“不該查出”的新數據

MySQL支持4種隔離性級別的設置:

MySQL事務隔離級別

隔離性最差的read uncommitted 會直接讀取最新的版本,而serializable會使用加鎖的方式。

剩下倆會通過MVCC(multi-version concurrency control)來實現,其主要依賴版本鏈和ReadView。

可以對比參照git的代碼版本控制和tag,事務對數據的修改就可以看成是各種分支,每一個事務有自己對應的版本位置,ReadView是在某個時刻對版本鏈打的tag,兩個級別的區別主要在于打tag的時機要求。

鎖的性質:共享鎖 pk 排他鎖

加鎖是隔離性最強的方法。對于讀和寫操作,我們對隔離性的要求又有一些不同:

由于讀操作不改變數據,所以A在讀的時候,并不排斥B也來讀;

寫操作會直接影響數據,所以A在寫的時候,即不能讓B來寫,也不能讓B來讀。

所以,讀操作的事務是可以同時進行的,而寫操作事務之間、寫操作和讀操作則是互斥排他的。

我們把讀操作加的鎖稱為共享鎖(S),寫操作加的鎖稱為排他鎖(X)。(p.s 讀操作也可以顯示聲明加排它鎖,select.....for update,一般不會這么干)

S鎖和X鎖的兼容性

加鎖、解鎖操作:

一個讀操作事務開始時,先判斷是否有X鎖,如果有則進入S鎖等待隊列等待,沒有則加上S鎖并開始執行;

一個寫操作事務開始是,先判斷是否有鎖,如有則進入X鎖等待隊列等待,沒有則加上X鎖并開始執行;

當前事務完成并釋放鎖后,先把X等待隊列中的X鎖出隊列執行操作(防止寫操作“饑餓”)。

鎖的顆粒度:表級鎖 pk 行級鎖

顧名思義,表級鎖是對整張表上鎖,而行級鎖是對一條記錄上鎖。一般情況下,行級鎖對并發的支持會好于表級所,比如有兩個寫任務,針對不同的記錄,行級鎖可以并行而表級鎖會阻塞。

不過說句公道話,表級鎖并不是一無是處,因為其更省內存。尤其是一個事務需要訪問表中大量數據或者做group by的時候,用表鎖只要維護一個鎖,而用行鎖要維護N個鎖。

此外,大量insert操作時,表鎖性能也會優于行鎖。InnoDB主鍵自增就是通過表級鎖完成的。

InnoDB引擎同時支持表級鎖和行級鎖,而其他引擎如MyISAM只支持表級鎖。

原因還是在于b+索引樹,InnoDB引擎可以對索引上鎖,當然同時也反過來要求使用行級鎖必須要用索引。

有行鎖和表鎖共存時就會引出一個問題,如果要對表加鎖,就需要判斷表中的記錄是否被加了行鎖,總不能遍歷查詢判斷每一行有沒有加鎖吧?

遍歷是不可能遍歷的,這輩子都不可能。為了解決這個問題,需要引入一個“意向”鎖。

意向鎖也分為共享和排他兩類,簡稱IS和IX。有IS鎖意味著表中有行級別S鎖;有IX鎖意味著表中有行級別X鎖。

意向鎖是表級別的,但其本身不鎖定任何表或者行,只是用來快速判斷表內是否有記錄被鎖著,所以意向鎖之間是兼容的。有10個意向鎖就說明里面有10個行級操作正在進行。

InnoDB表級別鎖的兼容性:

完整的鎖兼容性

完整版的上鎖過程:

行級讀操作,先判斷是否有表級X鎖或對應行的X鎖,若有則等待;沒有則給表加IS鎖,給對應行加S鎖并執行操作。

行級寫操作,先判斷是否有表鎖或者對應行鎖,若有則等待;沒有則給表加IX鎖,給對應行加X鎖并執行操作。

行級操作完成后,釋放行鎖及其加上的表意向鎖。

表級讀操作,先判斷是否有表級X鎖或IX鎖,若有則等待;沒有則給表加S鎖并執行操作。

表級寫操作,先判斷是否有表級鎖或意向鎖,若有則等待;沒有則給表加X鎖執行操作。

表級操作完成后,釋放表級鎖。

行級鎖的功能:記錄鎖 pk 間隙鎖

這二者還是比較好理解的。

記錄鎖鎖定的是一條數據記錄本身,對于臟讀、不可重復讀的情況,是某個特定的記錄被其他事務修改引起的,所以只要鎖定這提條數據防止其他事務來更新就可以。

對于幻讀,是由于新插入的記錄引起的,由于無法預測哪些記錄會插入,所以依靠鎖數據是無法避免的。這時需要再加一個間隙鎖。

間隙鎖的作用是鎖定上鎖行和主鍵前一條數據之間的空間,防止有插入操作。例如目前表中兩條連續記錄id分別是10和15,那在15的數據上加間隙鎖,可以防止插入id在11-14范圍內的數據。

supermum是mysql里的虛擬最大行記錄,對于當前表里主鍵最大的記錄,在supermum上加間隙鎖可以防止后續更大主鍵的記錄插入。

總結

以上是生活随笔為你收集整理的mysql 自身参照自身_MySQL入门的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。