sql的執(zhí)行順序:
- 1 FROM <left_table>
- 2 WHERE <where_condition>
- 3 GROUP BY <group_by_list>
- 4 HAVING <having_condition>
- 5 SELECT
- 6 ORDER BY <order_by_condition>
- 7 LIMIT <limit_number>
- 8 DISTINCT <select_list>
字段類型的取值范圍
- 1 bigint 從 -9223372036854775808 到 9223372036854775807 占 8 個字節(jié)
- 2 int 從-2,147,483,648 到 2,147,483,647 占 4 個字節(jié)
- 3 mediumint 從-8388608到8388607薄榛,無符號的范圍是0到16777215。 一位大小為3個字節(jié)
- 4 smallint 從-2^15 (-32,768) 到 2^15 - 1 (32,767) 的整型數(shù)據(jù)又谋,無符號的范圍是0到65535。一位大小占 2 個字節(jié)
- 5 tinyint 從-128--127爽撒,0~255胎挎。一位大小為 1 字節(jié)
- 6 varchar
varchar
字符類型若為gbk,每個字符最多占2個字節(jié)挟冠,最大長度不能超過32766;
字符類型若為utf8鹏漆,每個字符最多占3個字節(jié)巩梢,最大長度不能超過21845
一個字母或數(shù)字占用1個字節(jié)
varchar (30) 30--90字節(jié)
1個漢字占3個字節(jié)。
批量插入多條數(shù)據(jù)
insert into [tablename] (field1,field2...) values (value1,value12...)
批量插入語句
func insertData() {
data := []map[string]string{
{"name": "sos", "email": "87262420@qq.com"},
{"name": "膩?zhàn)?, "email": "297387990@qq.com"},
{"name": "領(lǐng)導(dǎo)", "email": "sosmyheart@163.com"},
}
sql := "insert into users(name, email) values"
var valuesList = []interface{}{}
for index, v := range data{
if index == len(data)-1{
sql += "(?,?)"
}else{
sql += "(?,?), "
}
valuesList = append(valuesList, v["name"], v["email"])
}
res, _ := db.Exec(sql, valuesList...) //批量數(shù)據(jù)!
mainId,_ := res.LastInsertId() //每插入一條數(shù)據(jù)就返回一個表的主鍵id
}
UNION
- 1 UNION ALL 會導(dǎo)致查詢的數(shù)據(jù)很多重復(fù)的
- 2 UNION 會做去重處理
- 3 聯(lián)合查詢的時候艺玲,字段列數(shù)要一致括蝠,而且字段的類型也要一致!
表1
A B
a 1
b 0
c 3
d 0
e 2
表2
A B
c 0
e 4
合并兩個表除去重復(fù)的數(shù)據(jù)(以表2的數(shù)據(jù)為主)饭聚,得到以下表
A B
a 1
b 0
c 0
d 0
e 4
select A,B from 表1 where A not in(select A from 表2)
union
select A,B from 表2
group by / order by 順序問題
- 1 先分組后排序
- 2 ORDER BY時不要使用SELECT *忌警,只查需要的字段。
having
- 1 檢索范圍不一致秒梳,它是在前者查詢完畢的基礎(chǔ)上再進(jìn)行檢索過濾法绵!用于對已分組的數(shù)據(jù)進(jìn)行過濾
- 2 跟where不是一個范圍,having是在where條件之上進(jìn)行篩選酪碘。
where
select * from table group by 字段 having 字段>10
having
select * from table having sum(字段)>100
聯(lián)合索引--最左匹配原則
1 最左匹配原則是針對聯(lián)合索引來的朋譬,索引的底層是一顆B+樹,構(gòu)建一顆B+樹只能根據(jù)一個值來構(gòu)建兴垦,當(dāng)索引類型為聯(lián)合索引時此熬,數(shù)據(jù)庫會依據(jù)聯(lián)合索引最左的字段來構(gòu)建B+樹,也叫最左匹配原則,聯(lián)合索引其實(shí)可以減少開銷犀忱。
2 最左優(yōu)先。以最左邊的為起點(diǎn)任何連續(xù)的索引都能匹配上扶关。同時遇到范圍查詢(>阴汇、<、between节槐、like)就會停止匹配搀庶。
3 查看是不是用到索引可以使用:EXPLAIN
EXPLAIN SELECT * from test ORDER BY a,b,c desc limit 10
- 最左匹配原則,可以是在where 或者 group by, order by 之后帶的字段都可以走索引铜异。
- 4 根據(jù)type字段來判斷是否使用索引哥倔,下面是性能的大小排列,ALL是最弱的揍庄!
- 5 index 類型的查詢雖然不是全表掃描, 但是它掃描了所有的索引, 因此比 ALL 類型的稍快.
ALL < index < range ~ index_merge < ref < eq_ref < const < system
6 索引列越多咆蒿,通過索引篩選出的數(shù)據(jù)越少。有1000W條數(shù)據(jù)的表蚂子,有如下SQL: select from table where col1=1 and col2=2 and col3=3沃测,假設(shè)假設(shè)每個條件可以篩選出10%的數(shù)據(jù),如果只有單值索引食茎,那么通過該索引能篩選出1000W10%=100w條數(shù)據(jù)蒂破,然后再回表從100w條數(shù)據(jù)中找到符合col2=2 and col3=3的數(shù)據(jù),然后再排序别渔,再分頁附迷;如果是聯(lián)合索引,通過索引篩選1000w10%10%10%=1w哎媚,效率提升可想而知
7 聯(lián)合索引就是按照第一列進(jìn)行排序喇伯,然后第一列排好序的基礎(chǔ)上再對第二列進(jìn)行排序,以此類推抄伍。如果沒有第一列直接訪問第二列艘刚,第二列肯定是無序的,直接訪問后面的列就用不到索引了截珍。
低粒度的鎖--RWMutex (讀寫鎖)
1 讀寫鎖--可以并發(fā)多個讀操作攀甚,但是寫是互斥的,當(dāng)goroutine進(jìn)行寫操作時岗喉,其他goroutine既不能讀也不能寫秋度,都是阻塞!
2 多個goroutine并發(fā)獲取讀鎖的時候钱床,有g(shù)oroutine要獲取寫鎖荚斯,這時候?qū)戞i的線程是阻塞的,后面如果還有g(shù)oroutine想要獲取讀鎖也是阻塞的~!
3 當(dāng)一個goroutine獲取寫鎖后事期,其他想要獲取讀鎖和寫鎖的都會阻塞
4 讀鎖是可重入鎖滥壕,同一個goroutine可以多次獲取讀鎖。同理兽泣,當(dāng)一個線程獲取到了寫鎖后嘗試獲取讀鎖绎橘,會造成死鎖錯誤。
5 讀寫鎖跟互斥鎖的區(qū)別在于:鎖的粒度減少唠倦,讀寫鎖可以讓多個goroutine獲取共享資源時同時獲取讀鎖称鳞。而互斥鎖在并發(fā)獲取共享資源的時候,只能讓一個goroutine獲取讀鎖稠鼻,其他goroutine阻塞冈止。影響性能!
sync.Mutex的操作
- 1 由于是并發(fā)的寫數(shù)據(jù),多個goroutine爭搶資源候齿,修改數(shù)據(jù)熙暴,最后count累加的結(jié)果并不是10000
func main() {
var count = 0
var wg sync.WaitGroup
//十個協(xié)程數(shù)量
n := 10
wg.Add(n)
for i := 0; i < n; i++ {
go func() {
//1萬疊加
for j := 0; j < 10000; j++ {
count++
}
defer wg.Done()
}()
}
wg.Wait()
fmt.Println(count)
}
- 2 加上互斥鎖后就能保證在寫操作的時候,保證沒有其他goroutine會篡改
func main() {
var count = 0
var wg sync.WaitGroup
//十個協(xié)程數(shù)量
n := 10
m := sync.Mutex{}
wg.Add(n)
for i := 0; i < n; i++ {
go func() {
//1萬疊加
for j := 0; j < 10000; j++ {
m.Lock()
count++
m.Unlock()
}
defer wg.Done()
}()
}
wg.Wait()
fmt.Println(count) 10000
}
sync.RWMutex
1 前提是明確知道當(dāng)下的應(yīng)用場景毛肋,是大量的讀和少量寫怨咪,就可以用RWMutex替代 Mutex
2 包括的方法: Lock/Unlock --- 針對寫操作,后面來的資源都會阻塞润匙,知道執(zhí)行Unlock()釋放鎖诗眨。
3 RLock/RUnlock:針對讀操作的方法,當(dāng)鎖被讀操作獲取的時候孕讳,RLock方法會直接返回匠楚。如果是被寫操作獲取的時候,RLock方法會一直阻塞厂财,直到RUnlock釋放鎖芋簿。
4 并發(fā)讀
func main() {
go reads(1)
go reads(2)
go reads(3)
time.Sleep(time.Second * 3)
}
func reads(num int) {
var m sync.RWMutex
fmt.Println("reads start")
m.RLock()
fmt.Println(num, "reads")
time.Sleep(time.Second * 2)
m.RUnlock()
fmt.Println(num, "reads end~!")
}
- 5 并發(fā)寫的場景, 寫的時候其他協(xié)程阻塞
func main() {
var m sync.RWMutex
for i := 1; i <= 3; i++ {
go writes(&m, i)
}
for j := 1; j <= 3; j++ {
go reads(&m, j)
}
time.Sleep(time.Second * 2)
fmt.Println("do work end~", count)
}
func reads(m *sync.RWMutex, num int) {
fmt.Println("reads start")
m.RLock()
fmt.Println("read下標(biāo):", num, count)
time.Sleep(1 * time.Millisecond)
m.RUnlock()
}
func writes(m *sync.RWMutex, j int) {
fmt.Println("write start")
m.Lock()
count++
fmt.Println("write下標(biāo):", j, count)
time.Sleep(1 * time.Millisecond)
m.Unlock()
}
// 控制臺輸出結(jié)果
write start
write start
write下標(biāo): 1 1 并發(fā)寫會阻塞
reads start
write start
reads start
reads start
read下標(biāo): 2 1 并發(fā)讀可以直接返回
read下標(biāo): 3 1
read下標(biāo): 1 1
write下標(biāo): 2 2
write下標(biāo): 3 3
do work end~ 3
事務(wù)
- 1 事務(wù)默認(rèn)是系統(tǒng)自動提交的!
- 2 事務(wù)的四大特性:原子性(不可拆分)璃饱,一致性(結(jié)果一致)与斤,隔離性(獨(dú)立操作),持久性(永久存儲在磁盤)
- 3 開始事務(wù)命令:start transaction 或者 begin
并發(fā)事務(wù)引發(fā)的問題
- 1 臟讀:一個事務(wù)讀到另一個事務(wù)沒有提交的數(shù)據(jù)
- 2 不可重復(fù)讀:一個事務(wù)先后讀取同一條記錄荚恶,但是2個結(jié)果不一致
- 3 幻讀:一個事務(wù)在查詢該條記錄時沒有發(fā)現(xiàn)數(shù)據(jù)存在撩穿,但是在插入數(shù)據(jù)的時候,又發(fā)現(xiàn)已經(jīng)存在谒撼,產(chǎn)生幻覺食寡,也就是幻讀。
事務(wù)的隔離級別就是解決事務(wù)并發(fā)的問題
- 1 read uncommited(讀未提交)廓潜,所有并發(fā)問題都會產(chǎn)生抵皱。
- 2 read commited (讀已提交)善榛,會產(chǎn)生不可重復(fù)讀和幻讀。
- 3 repeatable read(默認(rèn):可重復(fù)讀)呻畸,會產(chǎn)生幻讀的問題移盆。
- 4 serializable (串行化),可以解決所有并發(fā)問題伤为,但是性能是最慢的味滞。
Mysql 結(jié)構(gòu)
-
4層結(jié)構(gòu)
1709379398248.jpg - 存儲引擎是針對表的,不是庫的钮呀。
- 不同的引擎有不一樣的應(yīng)用場景。
- 5.5版本之后的Mysql默認(rèn)引擎是: InnoDB昨凡。
-
Memory引擎 : 數(shù)據(jù)是存放在內(nèi)存中爽醋,速度快,使用這個引擎的時候該表只能作為臨時表或者緩存來用便脊!
image.png
索引的數(shù)據(jù)結(jié)構(gòu)
- Innodb 引擎的索引是用B+Tree 結(jié)構(gòu):所有葉子節(jié)點(diǎn)存放數(shù)據(jù)蚂四,非葉子節(jié)點(diǎn)存索引,葉子節(jié)點(diǎn)形成雙向鏈表哪痰。所有數(shù)據(jù)都存在一個空間:表空間---段---區(qū)---頁---行遂赠,它們是層層包含的關(guān)系。
- Innodb的3大特性:事務(wù)晌杰,外鍵跷睦,行鎖
- 一個頁的空間大小是16KB
- B+Tree相比于二叉樹來說的優(yōu)點(diǎn):層級少,搜索速度快
索引分類
聚集索引掛的是表里的這一行的行數(shù)據(jù)
二級索引掛的是聚集索引的ID肋演,也就是通過這個id去聚集索引下面檢索數(shù)據(jù)抑诸,2個索引都是B+Tree結(jié)構(gòu)
-
下面這個查詢語句先走二級索引,找到對應(yīng)的鍵值再去聚集索引里找對應(yīng)的數(shù)據(jù)!這個過程叫做回表查詢爹殊!
image.png 指針比Key要多一個蜕乡,一個指針是占6字節(jié),而Key值要根據(jù)具體類型來計(jì)算梗夸,比如一個bigint占8字節(jié)层玲。
Query 查詢語句優(yōu)化
- 可以通過查詢語句查看sql語句的執(zhí)行具體情況,以便做性能優(yōu)化反症。
show global status like `Com______`; // 7個橫杠
Explain 檢測sql語句的性能
可以通過Explain 來查看Type字段下的索引是什么辛块,如果是All就代表查詢性能最差,可以對Sql進(jìn)行優(yōu)化惰帽。
創(chuàng)建索引命令: create index idx_sn on 表名(字段名)
最左匹配原則是針對聯(lián)合索引來的憨降,如果沒有走最左匹配就會全表掃描,用不上索引性能就會差一點(diǎn)该酗!如果查詢多個字段授药,并且跳過了索引中的列士嚎,那索引會部分失效,前提是走了聯(lián)合索引悔叽,也就是最左匹配走了莱衩。
-
當(dāng)出現(xiàn)比較范圍查詢時,最好加上 >= 可以規(guī)避索引失效問題!
image.png 盡量不要進(jìn)行函數(shù)運(yùn)算娇澎,否則索引會失效笨蚁。
-
查詢的時候,如果DB里存的是字符串趟庄,查詢語句沒有加引號括细,也會導(dǎo)致索引失效,檢索效率降低戚啥。
image.png -
頭部模糊匹配奋单,索引會失效。
1709555920234.jpg
索引提示性使用
- use index (標(biāo)識使用具體那個索引)
- ignore index (忽略使用某索引)
- force index (強(qiáng)制使用某索引)
Explain select * from user ignore index(索引名) where phone="xxxx"
- 覆蓋索引:就是回表查詢的時候一次就查詢到數(shù)據(jù)猫十,不需要再去聚集索引檢索數(shù)據(jù)
這種情況要優(yōu)化的話览濒,最好是建立聯(lián)合索引走覆蓋索引檢索數(shù)據(jù),不需要回表二次查詢拖云,性能是最優(yōu)贷笛!
- select id,username,password from user where password ="xxxx"
- 前綴索引:是用在查詢某字段,因數(shù)據(jù)太長或者文本體積太大宙项,而截取其中一小段建立索引乏苦,來提高檢索速度的!
Limit優(yōu)化
-
可以采用子查詢的方式進(jìn)行優(yōu)化(數(shù)據(jù)量特別大的時候)
image.png
Count優(yōu)化
- 遇到數(shù)據(jù)量大的情況杉允,count可以存redis邑贴,每次插入一條數(shù)據(jù)就累加,自己維護(hù)一個總數(shù)叔磷,當(dāng)數(shù)據(jù)量大時去Mysql查詢會比較慢
- count查詢的效率對比:
count(字段) < count(主鍵id) < count(1) < count(*) # count(*)是最快的拢驾,Innodb專門針對count(*)進(jìn)行了優(yōu)化,不需要累加計(jì)數(shù)
Update 優(yōu)化
- Innodb的行鎖是針對索引加的改基,不是針對記錄加的鎖繁疤,并且索引不能失效,否則行鎖會升級成表鎖秕狰,也就是當(dāng)insert操作的時候會鎖住一整張表稠腊,其他操作提交不了。并發(fā)性能就會降低
按照鎖的粒度劃分
全局鎖:開啟全局鎖時鸣哀,不能進(jìn)行增刪改操作架忌,只能進(jìn)行查詢操作。
表級鎖:表鎖又細(xì)分寫和讀鎖我衬,同樣開啟讀鎖是叹放,其他DML操作阻塞饰恕,開啟寫鎖時,其他的DML和DQL都不能操作井仰。
行級鎖: 行鎖鎖的是索引埋嵌,不是葉子節(jié)點(diǎn)的數(shù)據(jù)。
當(dāng)操作的字段沒有索引俱恶,開啟事務(wù)操作的時候行鎖就會升級成表鎖雹嗦,其他操作會阻塞。