一次搞定!整理 volley 和 retrofit2 如何傳送 cookie id 資料的
資料庫三大設計正規化
-
第一正規化 1NF:資料表的所有欄位都是不可拆分的原子值。
比如:地址可以拆分為 國家、省份、市區、街道、門牌號等。不應該寫到一起。方便統計。
-
第二正規化 2NF:滿足 1NF 前提下,主鍵外的每一列都必須完全依賴於主鍵。如果不完全依賴,只可能是聯合主鍵的情況。
create table myorder( product_id int, customer_id int, product_name varchar(20), customer_name varchar(20), primary key(product_id, customer_id) );
除主鍵外的其他列,只依賴於主鍵的部分欄位。pName之和pId有關,cName之和cId有關。
拆表。
create table myorder( order_id int primary key, product_id int, customer_id int, ); create table product( id int primary key, name varchar(20), ); create table customer( id int primary key, name varchar(20), );
-
第三正規化 3NF:滿足 2NF 前提下,除主鍵列外其他列之間不允許有傳遞依賴關係。
create table myorder( order_id int primary key, product_id int, customer_id int, customer_phone varchar(15) );
cPhone和cID是唯一相關的,產生了冗餘。
事務管理
**事務定義:**一個最小的不可分割的工作單元。能夠保證一個業務的完整性。
儲存引擎(表型別?):在mysql的資料用不同的技術儲存在檔案(記憶體)中。
- show engines檢視。
- 最多是innodb,myisam,memory等。innodb支援事務,myisam,memory不支援。
基本術語:
- 事務(transaction)指一組 SQL 語句;
- 回退(rollback)指撤銷指定 SQL 語句的過程;
- 提交(commit)指將未儲存的 SQL 語句結果寫入資料庫表;
- 保留點(savepoint)指事務處理中設定的臨時佔位符(placeholder),你可以對它釋出回退(與回退整個事務處理不同)。
事務四大特徵ACID
A:原子性atomicity:事務是最小的單位,不可分割。同一事務的sql語句必須保證同時成功或同時失敗。
C:一致性consistency:資料庫在事務執行前後都保持一致性狀態,事務開始和結束之間的中間狀態不會被其他事務看到。事務的修改必須滿足相關的資料規則,保證資料完整性。事務結束後,所有內部資料結構(B樹索引和雙向連結串列)必須是正確的。所有事務對同一個資料的讀取結果都是相同的。
I:隔離性isolation:保證事務執行能不被其他併發操作影響獨立執行,即事務內部的操作和使用的資料對併發的其他事務是隔離的。
D:永續性durability:事務一旦結束提交,會永久的改變資料庫,即使出現系統故障也能保持。
事務隔離性:隔離級別越高,效能越差。
- 更新丟失,a、b同時修改一張表,只保留了最後一次結果。在一個事務未提交前,另一個事務不能訪問同一檔案。
- read uncommitted:讀未提交的——髒讀。b讀到a 已修改但未提交的資料。違反一致性
- read committed:讀已提交的——不可重複讀。b讀到a 已修改並提交的資料。導致b認為前後不一致。違反隔離性。
- repeatable read:可以重複讀——幻讀。預設級別。b讀到a 提交的新增資料。a插入資料,導致b兩次讀到的資料不同。b讀不到a已經提交的資料,導致插入操作出錯。違反隔離性。
- serializable:序列化。當表被事務a操作時,其他事務b的寫操作會卡住(會等待超時),進入排隊狀態。當事務a結束後,事務b的寫操作才會執行。效率很低。
查詢隔離級別:8.0用transaction_isolation; 5.0用tx_isolation
- 系統級別:select @@global.transaction_isolation;
- 會話級別:select @@transaction_isolation;
- 修改系統:set global transaction isolation level read uncommitted;
- 修改會話:set session transaction isolation level read uncommitted;
事務一致性:
- 強一致性:讀操作可以立即讀到提交的更新操作。
- 弱一致性:提交的更新操作,不一定立即會被讀操作讀到,此種情況會存在一個不一致視窗,指的是讀操作可以讀到最新值的一段時間。
- 最終一致性:是弱一致性的特例。事務更新一份資料,最終一致性保證在沒有其他事務更新同樣的值的話,最終所有的事務都會讀到之前事務更新的最新值。如果沒有錯誤發生,不一致視窗的大小依賴於:通訊延遲,系統負載等。
- 其他一致性變體還有:
- 單調一致性:如果一個程序已經讀到一個值,那麼後續不會讀到更早的值。
- 會話一致性:保證客戶端和伺服器互動的會話過程中,讀操作可以讀到更新操作後的最新值。
事務原子性
-
實現事務的原子性,要支援回滾操作,在某個操作失敗後,回滾到事務執行之前的狀態。
-
回滾實際上是一個比較高層抽象的概念,大多數DB在實現事務時,是在事務操作的資料快照上進行的(比如,MVCC),並不修改實際的資料,如果有錯並不會提交,所以很自然的支援回滾。
-
在其他支援簡單事務的系統中,不會在快照上更新,而直接操作實際資料。可以先預演一邊所有要執行的操作,如果失敗則這些操作不會被執行,通過這種方式很簡單的實現了原子性。
自動提交:
show variables like '%autocommit%';
set autocommit=0;
START TRANSACTION
# sql語句。只針對增刪改查,create、drop不行。
SAVEPOINT delete1
// ...
ROLLBACK TO delete1
// ...
COMMIT
注意:
- 不能回退 SELECT 語句,回退 SELECT 語句也沒意義;
- 不能回退 CREATE 和 DROP 語句。
- truncate不能回滾,delete可以。
- MySQL 的事務提交預設是隱式提交,每執行一條語句就把這條語句當成一個事務然後進行提交。當出現 START TRANSACTION 語句時,會關閉隱式提交;當 COMMIT 或 ROLLBACK 語句執行後,事務會自動關閉,重新恢復隱式提交。
- 設定 autocommit 為 0 可以取消自動提交;autocommit 標記是針對每個連線而不是針對伺服器的。
- 如果沒有設定保留點,ROLLBACK 會回退到 START TRANSACTION 語句處;如果設定了保留點,並且在 ROLLBACK 中指定該保留點,則會回退到該保留點。
案例
比如a給b轉賬100:
update user set money=money-100 where name='a';
update user set money=money+100 where name='b';
要求兩個語句必須同時成功或者同時失敗。
- mysql預設開啟事務(自動提交)select @@autocommit; – 1
- 開始事務時,執行sql語句後立即生效,不能回滾。 rollback;–無效
- 關閉事務:set autocommit=0;
- 關閉事務時,執行sql語句後,需要手動提交才不能回滾。commit;
- 在沒有commit前,rollback會回滾所有語句。所以轉賬時,可以兩條語句都執行完畢後再commit。
**手動開啟事務:**在最開始,輸入begin; 或者 start transaction;此後可以rollback;
邏輯架構
應用程式——連線池——快取(讀資訊)和緩衝(寫資訊)——讀不到資訊,到sql介面——解析器——優化器,生成執行計劃——儲存引擎——返回客戶端,並在快取存一份。
啟動快取:
- 修改配置檔案:vim /etc/my.cnf
- 新增 query_cache_type=1 (修改字元為:character_set_server=utf8)
- esc —— :wq
- 重啟mysql:systemctl restart mysqld
- 檢查一下是否active:systemctl status mysqld
- 開啟profile:
- show variables like ‘%profiling%’
- set profiling=1
- 執行語句 select。。。
- 檢視profiles
- show profiles; ——顯示語句id,耗時,命令
- show profile cpu.block io for query 語句id; ——status。兩次檢視可以看到第二次查詢時,直接在快取中查詢。(時間變短)
- 想要一樣,必須p(sql語句)、v(查詢結果)鍵值對完全一致。
儲存引擎
show engines; ——最常用 InnoDB 和 MyISAM
分類:
- InnoDB :mysql的預設事務型引擎,用來處理大量短期事務。優先使用。
- MyISAM:提供大量特性,如全文索引、壓縮、空間函式GIS等。不支援事務和行級鎖。崩潰後無法安全恢復
- Archive:檔案儲存引擎支援insert和select操作。適合日誌和資料採集類應用。比MyISAM表小75%,比InnoDB 表小83%。
- Blackhole:沒有實現任何儲存機制,它會丟棄所有插入資料,不做任何操作。伺服器會記錄Blackhole表的日誌,可以用於複製資料到備庫,或者簡單地記錄到日誌。這種應用方式會有很多問題。
- CSV:可以作為普通的csv檔案作為MySQL表來處理,不支援索引。可以作為資料交換機制。用於報表系統,文字。
- Memory:用於快速訪問資料,且資料不會修改,不在意重啟資料丟失。
- Federated:訪問其他MySQL資料庫的一個代理,很多問題,預設禁用。
InnoDB 和 MyISAM的區別:
MyISAM | InnoDB | |
---|---|---|
外來鍵 | 不支援 | 支援 |
事務 | 不支援 | 支援 |
行表鎖 | 表鎖,即使操作一條記錄也會鎖住整個表,不適合高併發 | 行鎖,操作時只鎖住某一行,不對其他行有影響,適合高併發(會出現死鎖) |
快取 | 只快取索引,不快取真實資料 | 快取索引和真實資料,對記憶體要求高,記憶體大小對效能有決定性影響 |
關注 | 節省資源、消耗少、簡單業務 | 併發寫、事務、更大資源 |
預設安裝 | 是 | 是 |
預設使用 | 否 | 是 |
系統表 | 是 | 否 |
詳細版:
MyISAM | Innodb | |
---|---|---|
儲存結構 | 每張表被存放在三個檔案:frm-表格定義、MYD(MYData)-資料檔案、MYI(MYIndex)-索引檔案 | 所有的表都儲存在同一個資料檔案中(也可能是多個檔案,或者是獨立的表空間檔案),InnoDB表的大小隻受限於作業系統檔案的大小,一般為2GB |
儲存空間 | MyISAM可被壓縮,儲存空間較小 | InnoDB的表需要更多的記憶體和儲存,它會在主記憶體中建立其專用的緩衝池用於高速緩衝資料和索引 |
可移植性、備份及恢復 | 由於MyISAM的資料是以檔案的形式儲存,所以在跨平臺的資料轉移中會很方便。在備份和恢復時可單獨針對某個表進行操作 | 免費的方案可以是拷貝資料檔案、備份 binlog,或者用 mysqldump,在資料量達到幾十G的時候就相對痛苦了 |
檔案格式 | 資料和索引是分別儲存的,資料.MYD ,索引.MYI | 資料和索引是集中儲存的,.ibd |
記錄儲存順序 | 按記錄插入順序儲存 | 按主鍵大小有序插入 |
外來鍵 | 不支援 | 支援 |
事務 | 不支援 | 支援 |
鎖支援(鎖是避免資源爭用的一個機制,MySQL鎖對使用者幾乎是透明的) | 表級鎖定 | 行級鎖定、表級鎖定,鎖定力度小併發能力高 |
SELECT | MyISAM更優 | |
INSERT、UPDATE、DELETE | InnoDB更優 | |
select count(*) | myisam更快,因為myisam內部維護了一個計數器,可以直接調取。 | |
索引的實現方式 | B+樹索引,myisam 是堆表 | B+樹索引,Innodb 是索引組織表 |
雜湊索引 | 不支援 | 支援 |
全文索引 | 支援 | 不支援 |
MyISAM索引與InnoDB索引的區別?
- InnoDB索引是聚簇索引,MyISAM索引是非聚簇索引。
- InnoDB的主鍵索引的葉子節點儲存著行資料,因此主鍵索引非常高效。
- MyISAM索引的葉子節點儲存的是行資料地址,需要再定址一次才能得到資料。
- InnoDB非主鍵索引的葉子節點儲存的是主鍵和其他帶索引的列資料,因此查詢時做到覆蓋索引會非常高效。
InnoDB引擎的4大特性
- 插入緩衝(insert buffer)
- 二次寫(double write)
- 自適應雜湊索引(ahi)
- 預讀(read ahead)
儲存引擎選擇
如果沒有特別的需求,使用預設的Innodb
即可。
MyISAM:以讀寫插入為主的應用程式,比如部落格系統、新聞入口網站。
Innodb:更新(刪除)操作頻率也高,或者要保證資料的完整性;併發量高,支援事務和外來鍵。比如OA自動化辦公系統。
索引優化
效能下降
- 資料太多——分庫分表
- 關聯太多表,太多join——sql優化
- 沒有充分利用索引
- 伺服器調優及各個引數設定——調整my.cnf
索引(Index):
- 幫助MySQL高效獲取資料的資料結構。 可以簡單理解為排好序的快速查詢資料結構
- 索引本身也很大,不可能全部存到記憶體中,往往以索引檔案的形式存到磁碟上
- **優點:**提高資料檢索效率,降低IO成本。索引對資料進行排序,降低排序成本,CPU消耗。
- **缺點:**提高了查詢速度,降低更新、插入、刪除的速度(需要同步更新索引資訊)。索引也是一張表,儲存了主鍵與索引欄位,並指向實體表的記錄,佔用空間。
索引結構:平衡樹(二叉樹在極端情況下變成連結串列,平衡樹會旋轉)
- BTree:
- B+Tree:發生的IO次數更低。
- 聚簇索引(資料行和相鄰鍵值聚簇的儲存在一起)和非聚簇索引
索引分類:
- 單值:一個索引只包含單個列,可以有多個
- 唯一:索引列的值必須唯一,允許空值。可以有多個null
- 主鍵:設定為主鍵,資料庫會自動建立索引,InnoDB為聚簇索引
- 複合:一個索引包括多個列
索引語法:
- 檢視:show index from 表名;
- 建立:create [unique] index [索引名] on 表名(欄位名);
- 修改:alter table 表名 add unique 索引名(列);
- 刪除:drop index [索引名] on 表名
什麼時候用索引:
- 主鍵自動建立唯一索引
- 頻繁查詢的欄位應該建立索引
- 查詢與其他表關聯的欄位應該建立索引,外來鍵
- 組合索引比單鍵索引價效比高。MySQL只會選擇一個索引,組合索引才能都用起來。
- 排序欄位應該建立索引
- 統計或者分組欄位
不用索引
- 記錄很少
- 經常刪改
- where中用不到的欄位
- 過濾性不好的欄位。如性別
explain
模擬優化器執行SQL語句,分析查詢語句或表結構的效能瓶頸。
- 功能:
- 表的讀取順序
- 哪些索引可用
- 資料讀取操作的操作型別
- 哪些索引被引用
- 表之間的引用
- 每張表多少行被物理查詢
- 怎麼做
- explain sql語句(select…)
- 總:id一個最好。type,檢視是否為all或者range。key_len,索引長度,越長越好。rows:越短越好。extra,order、group、union等。
- id:id一樣從上到下,id不一樣(子查詢),id越大優先順序越高,先執行。每一個id號表示一次獨立的查詢,一個sql的查詢次數越少越好。
- select_type:
- simple:簡單查詢,不包含子查詢或union
- primary:主要查詢,查詢中包含複雜的子部份,最外層標記為primary
- derived:衍生查詢,在from中包含的子查詢標記為derived,MySQL會遞迴執行這些子查詢,並把結果存到臨時表中。
- subquery:子查詢,在select或where中包含子查詢,用=連線。
- dependent subquery:依賴子查詢,在select或where中包含子查詢,用in連線。
- uncacheable subquery:不可用快取的子查詢,sql一模一樣就可以用快取,若sql不可能一樣,則標記為uncacheable 。如包含系統變數
- union:聯合查詢,在第二個select出現在union後,標記為union。若union包含在from子句的子查詢中,外出標記為derived
- union result:聯合查詢結果,從union表獲取的結果。
- table:顯示錶名 或(臨時表)
- partitions:代表分割槽表的命中情況,非分割槽表,該項為null
- type:訪問型別排列。顯示查詢使用的型別,system>const>eq_ref>ref>range>index>all。最差應該達到range,最好是到ref。
- system:表中只有一行記錄(等於系統表),一般不會出現
- const:通過索引一次就找到,用於比較primary key或unique索引。(索引值為常量,id=1)
- eq_ref:唯一性掃描索引,對每一個索引鍵,表中只有一條記錄與之匹配。(索引值為主鍵或唯一鍵)
- ref:非唯一性索引掃描,返回匹配某個單獨值的所有行。本質上是一種索引訪問,但可能會找到多個符合條件的行,屬於查詢和掃描的混合體(用=連線,且不是主鍵或唯一鍵)
- range:範圍查詢,只檢測給定範圍的行,使用一個索引來選擇行,key列顯示使用了哪個索引。一般在where中出現between、<、>、in等查詢。
- index:全索引掃描,只遍歷索引樹。通常索引檔案比資料檔案小,所以index比all快。index和all都是讀全表,但是index是從索引讀取,all從硬碟讀取。一般是使用了 覆蓋索引 或者 利用索引進行了排序分組。
- all:全表掃描,效率極低,必須建立索引
- index_merge:查詢需要多個索引組合。(用到or連線)
- ref_or_null:某個欄位需要關聯條件,也需要null的情況。(or連線,且其中一個為null)
- index_subquery:子查詢用到索引,不需要關聯掃描
- unique_subquery:子查詢中唯一索引。(主鍵或唯一鍵)
- possible_keys:顯示可能應用在這張表中的索引,一個或多個 。查詢涉及到的欄位若存在索引,則將給索引列出。但是不一定被查詢使用。
- key:實際使用的索引。如果為null,沒有用索引。查詢中若使用了覆蓋索引,僅顯示在key列表中,possible_keys沒有。
- key_len:索引長度。where後面的篩選條件命中索引的長度,越小越好。如,int長度4位元組,加上null,5位元組。varchar(20)長度20位元組,gbk*2,utf8*3,動態字元最後加2位元組。允許為空的字元加1位元組。
- ref:顯示索引的哪一列被使用。哪些列或常量被用於查詢索引列上的值。(沒多少意義)
- rows:MySQL認為執行查詢時必須檢查的行數,越小越好。模擬值。
- filtered:儲存引擎返回的資料中server層過濾後,剩下滿足查詢記錄數量的比例。百分比。
- extra:包含不適合在其他列中顯示但十分重要的額外資訊。(order by、group by)
- using filesort:檔案排序,order by沒用上索引。
- using temporary:新建臨時表儲存中間結果。group by沒用上索引。group by包含order by,更慢。
- using index:使用了覆蓋索引,避免訪問了表的資料行,效率不錯。同時出現using where表明索引被用來執行索引鍵值的查詢。沒有出現using whereusing where表明索引只是用來讀取資料而非利用索引執行查詢。
- using where:使用了where過濾
- using join buffer:union沒用上索引。關聯欄位增加索引(on後面)。
- impossible where:邏輯錯誤。
- select tables optimized away:沒有group by的情況下,基於索引優化min/max操作或者對於MyISAM儲存引擎優化count(*)操作。查詢執行計劃生成的階段即完成優化。
- distinct:優化distinct操作,在找到第一匹配的元組後,停止再找相同值的操作。
覆蓋索引:covering index
- select的資料列只用從索引中取得,不必讀取資料行。MySQL可以利用索引返回select列表中的欄位,不必根據索引再次讀取資料檔案。查詢列被索引覆蓋。
- 注意:
- 要用覆蓋索引,select列表中不要用*,而只取需要的列。
- 如果將所有欄位一起做成索引,會導致索引檔案過大,查詢效能下降。
單表使用索引和常見索引失效
1、改變順序不影響用到索引,優化器會優化。但是缺少了索引開頭的欄位,後面的就用不了。
2、最佳左字首法則。索引建立平衡樹是分層的。先建立age樹,後建立deptid樹。deptid樹的建立是在age相同的情況下的。
3、函式或計算索引失效。如 name like '張%'能用到, left(name, 1)='張'用不到索引
4、範圍查詢後面的欄位失效。如:where name='abc' and deptid>4 and age=35; # age失效。建立索引時範圍查詢應該放到後面。如日期、時間、能比較的欄位等。
5、不等於索引失效。如:where name<>'abc'。
6、 is not null索引失效。 is null可以用。
7、模糊查詢首字元未知,索引失效,索引建立根據字元順序的。如: like '%abc%'; 可以用覆蓋索引優化。 like是範圍,但是不會導致後面的失效。
8、自動或手動型別轉換索引失效。如:name=123,如果 name是 varchar,匹配 int失效。
9、用 or之後索引失效。
# 沒有索引,全表掃描all
explain select sql_no_cache * from emp where emp.age=30; # sql_no_cache不走cache
create index idx_age on emp(age); # 為age建立索引,type變為ref,key_len,rows變化。
explain select sql_no_cache * from emp where emp.age=30 and drptid=4; # 增加條件
create index idx_age_deptid on emp(age, deptid); # 聯合索引,ref
一般性建議:
- 單鍵索引儘量選擇當前query過濾性更好的索引。性別過濾性不好,學號、身份證、手機號好。跟業務不相關的自增最好。
- 選擇組合索引時,當前query中過濾性最好的欄位在索引欄位順序越靠前越好
- 選擇組合索引時,儘量選擇可以包含當前query中where字句中更多欄位的索引
- 選擇組合索引時,如果某個欄位可能出現範圍查詢,應該放到最後
- 避免索引失效
關聯查詢優化
- 關聯查詢分為:驅動表、被驅動表。驅動表必須全表掃描,建立索引無效。被驅動表建立索引有效。
- 驅動表和被驅動表會換順序,在inner join中,會根據是否有索引更換,優化查詢,left join就沒辦法了。
- straight_join指定驅動表和被驅動表,不可以換,以免大表中有主鍵,導致mysql選擇大表作為被驅動表。使用時,必須明確表的數量級關係不會變。
- 小表為驅動表,大表為被驅動表。提高效率。
- 虛擬表(子查詢的結果)不能建立索引,不能放到被驅動表上。
- 能直接關聯儘量直接關聯,不要用子查詢。子查詢多趟查詢。
子查詢優化:進行不要用not in或not exists。
- 一個表有,另一個表沒有,用關聯查詢代替
# 查詢非ceo資訊,emp表有資訊和id,dept有ceo的id。
select * from emp a
where a.id not in (
select b.ceo from dept b
where b.ceo is not null
);
# 優化:不用子查詢,is not null改為is null
select * from emp a
left join dept b
on a.id=b.ceo
where b.id is null
# 小表驅動大表,in 和 exists
select * from A where id in ( select id from B)# 當A表大於B表時,in好
select * from A where exists( select 1 from B where A.id=B.id) # A表小於B表時,exists好
1、 exists(subquery) 返回 True或 False,子查詢列表可以為1或其他常數。程式碼會忽略select清單。
2、 exists子查詢往往可以用條件表示式、其他子查詢、join代替。
3、 exists子查詢實際執行可能會被優化,需要實際檢驗確定效率問題。
排序分組優化
index(age,birth) # 檢視是否using filesort
1、where age>20 order by age; # 沒有
2、where age>20 order by age,bitrh; # 沒有
3、where age>20 order by bitrh; # 有
4、where age>20 order by bitrh,age; # 有
# 結論:範圍查詢會破壞索引的排序功能
order by用到索引的條件:
- 必須有過濾條件,where或者limit。
- 排序欄位的順序必須和索引一致,因為order by調整順序的結果不一樣,不能更換順序。
- 升降序必須只選一個。都升序或都降序。
group by和order by幾乎一樣。但是group by即使沒有過濾條件,也可以直接用索引。
filesort有兩種演算法
- 雙路排序:MySQL4.1之前,掃描兩次磁碟。從磁碟讀行指標和order列,在buffer對他們排序,然後掃描已經排序好的列表。按照列表的值重新從磁碟中讀取資料輸出。
- 單路排序:從磁碟讀取需要的所有列,在buffer對order列排序,掃描排序後的列表輸出。避免二次讀取資料,把隨機IO變成順序IO,效率高。但是會佔用更多空間,而且一旦超出sort_buffer容量,會導致多次IO,效能更差。
- 增加sort_buffer_size
- 增加max_length_for_sort_data
提高order by的速度:
- select * 只query需要的欄位。
- query欄位大小總和小於max_length_for_sort_data,且排序欄位不是text|blob型別時,才用單路排序,否則用多路排序。
- 兩種演算法都可能超出sort_buffer容量,超出後,會建立tmp檔案進行合併排序,導致多次I/O,但單路排序風險更大,需要提高sort_buffer_size。
- 嘗試提高sort_buffer_size。這個引數針對每個程序的1M-8M之間調整。
- 嘗試提高max_length_for_sort_data。會增加用改進演算法的概率。如果太高,資料總容量超出sort_buffer_size的概率增大,導致高磁碟I/O活動和低處理器效率。1024-8192之間調整。
**覆蓋索引:**不要用select *,寫出需要的具體欄位。
# 組內排序:
找emp員工表中年齡第二大的人。
set @rank=0;
set @last_deptid=0;
select a.deptid, a.name, a.age
from (
select t.*, if(@last_deptid=deptid, @rank:=@rank+1, @rank:=1) as rk,
@last_deptid:=deptid as last_deptid
from emp t
order by deptid, age desc
) a
where a.rk=2;
慢查詢日誌
使用
- 檔案在 /var/lib/mysql/主機名-slow.log
- MySQL提供的日誌記錄,用來記錄在MySQL中響應時間超過閾值的語句,具體指執行時間超過long_quey_time值的sql。long_quey_time預設為10,指10s。
- 預設關閉:show variables like ‘%slow_query_log%’;
- 開啟:set global slow_query_log=1;
- 修改配置檔案永久生效。my.cnf 的 [mysqld]下增加 slow_query_log=1 和 slow_query_log_file=/var/lib/mysql/主機名-slow.log
- 閾值時間:show variables like ‘long_quey_time%’;
- 修改閾值:set long_quey_time=0.1;
- 檢視記錄:cat /var/lib/mysql/主機名-slow.log
- 查詢當前有多少慢查詢記錄:show global status like ‘%Slow_queries%’
日誌分析工具
-
mysqldumpslow。 # 用來進行篩選,分析等。
-
mysqldumpslow --help # 檢視幫助
-
mysqldumpslow -s c -t 3 -a 日誌檔案 # 計算c個數、t時間,t顯示top3,a顯示數字和字元
-
s:何種方式訪問,c:訪問次數,I:鎖定時間,r:返回記錄,t:查詢時間,al:平均鎖定時間,ar:平均返回記錄數,at:平均查詢時間,t:返回前面多少條資料,g:搭配正則表示式,大小寫不敏感等。
-
可以配合管道和more
show profile
- mysql提供的分析會話中語句執行的資源消耗情況。用於sql調優的測量。
- 預設關閉,並儲存最近15次執行結果。show variables like ‘%profiling%’;
- 開啟功能:set profiling=on;
- 檢視結果:show profiles; # 展示query_id,耗時、sql程式碼
- 診斷sql:show profile cpu, block io for query query_id; # sql的完整生命週期和過程
- converting HEAP to MyISAM查詢結果太大,記憶體不夠用,往磁碟搬了。
- Creating tmp table 建立臨時表
- 拷貝資料到臨時表
- 用完再刪除
- Copying to tmp table on disk 把記憶體的臨時表複製到磁碟。
- locked
全域性查詢日誌
- 啟用:set global general_log=1; set global log_output=‘TABLE’;
- 檢視:select * from mysql.general_log;# mysql 系統表
- 永遠不要在生產環境開啟
程序
- 展示程序列表:show processlist;
- 殺掉程序:kill 程序id
資料鎖
鎖是計算機協調多個程序或執行緒併發訪問某一資源的機制。
資料庫中,除了傳統計算資源(cpu、ram、io等)爭用外,資料也是一種使用者共享資源。保證資料併發訪問一致性、有效性是資料庫必須解決的問題。
分類
- 資料操作型別
- 讀鎖(共享鎖 Shared)S鎖:對同一份資料,多個讀操作可以同時進行、互不影響。
- 寫鎖(互斥鎖 Exclusive)X鎖:當前寫操作沒完成前,阻斷其他寫鎖和讀鎖。
- 資料操作粒度:行鎖、表鎖
三鎖:(開銷、加鎖速度、死鎖、粒度、併發效能)
-
引擎
- InnoDB實現行級鎖定,效能損耗比表級鎖定高一些。但是在高併發情況下,行級鎖定就有明顯優勢。
- MyISAM在查詢時,會自動給涉及的表加讀鎖,在執行增刪改操作前,會自動給涉及的表加寫鎖。
-
cap:強一致性、高可用性、分割槽容錯性。p必須保證,ca只能選一個
-
表鎖:偏讀
- 特點:偏向MyISAM儲存引擎,開銷小,加鎖快,無死鎖,粒度大,發生鎖衝突概率最高,併發度最低
- 建表:create table 表名 (欄位名 資料型別 鍵)engine myisam;# 指定儲存引擎
- 增加表鎖:lock table 表名 read/write,表名2 read/write,…;
- 讀鎖:本會話,可讀被鎖表,不能讀其他表。不能寫操作。其他會話,可讀被鎖表,可讀其他表,寫操作進入阻塞狀態。
- 寫鎖:本會話,可讀被鎖表,不能讀其他表,可寫被鎖表。其他會話,讀寫操作都會進入阻塞狀態。
- 查看錶鎖:show open tables; # in_use 為1表示鎖,0表示m
- 刪除表鎖:unlock tables;
- MyISAM在查詢時,會自動給涉及的表加讀鎖,在執行增刪改操作前,會自動給涉及的表加寫鎖。
- 分析表鎖定:show status like ‘table%’;
- table_locks_immediate:立即釋放表鎖次數。
- table_locks_waited:需要等待的表鎖數,表示表級鎖的爭用情況。
- MyISAM的讀寫鎖排程是寫優先,不適合作為主表的引擎。寫鎖後,其他執行緒不能操作,如果有大量更新操作會使查詢阻塞。
- 如果Table_locks_immediate / Table_locks_waited > 5000,最好採用InnoDB引擎,
因為InnoDB是行鎖而MyISAM是表鎖,對於高併發寫入的應用InnoDB效果會好些。
-
行鎖:偏寫
- 特點:偏向InnoDB儲存引擎,開銷大,加鎖慢,會出現死鎖,粒度小,發生鎖衝突概率最低,併發度最高。
- InnoDB對比MyISAM最大不同:InnoDB支援事務(transaction),並採用行級鎖。
- 建表:create table 表名 (欄位名 資料型別 鍵)engine innodb;# 指定儲存引擎
-
關閉自動提交:set autocommit=0;
-
更新某一行但不提交,其他會話該行被鎖定,進入阻塞。
-
無索引導致行鎖升級為表鎖。比如,本會話執行時,出現了索引失效(函式、型別轉換等),導致整張表都被鎖起來了。
-
間隙鎖:當使用範圍條件檢索時,範圍內已有資料的索引項加鎖,範圍內不存在的記錄叫做“間隙GAP”。innodb也會對這個間隙加鎖,稱之為間隙鎖Next-Key鎖。其他會話操作間隙鎖也會進入阻塞。
-
單獨一行加鎖。
- select … for update。# 鎖定一行,其他會話不能讀寫。
- select … lock in share mode; # 鎖定一行,其他會話不能讀。
-
分析表鎖定:show status like ‘innodb_row_lock%’;
- innodb_row_lock_current_waits:當前鎖定數量
- innodb_row_lock_waits:總共等待次數
- innodb_row_lock_time:總鎖定時間
- innodb_row_lock_time_avg:平均等待時間
- innodb_row_lock_time_max:最長的一次等待時間
-
優化建議:
- 資料檢索儘可能用索引完成,避免行鎖升級為表鎖
- 儘量縮小鎖的範圍
- 儘量少的檢索條件,避免間隙鎖
- 儘量控制事務大小,減小鎖定的資源和時間
- 儘量低事務隔離級別
-
-
頁鎖:
- 開銷和加鎖時間、粒度、併發度在表鎖和行鎖中間,會出現死鎖。
封鎖協議
1. 三級封鎖協議
一級封鎖協議:事務 T 要修改資料 A 時必須加 X 鎖,直到 T 結束才釋放鎖。可以解決丟失修改問題,因為不能同時有兩個事務對同一個資料進行修改,那麼事務的修改就不會被覆蓋。
二級封鎖協議:在一級的基礎上,要求讀取資料 A 時必須加 S 鎖,讀取完馬上釋放 S 鎖。可以解決髒讀問題,因為如果一個事務在對資料 A 進行修改,根據 1 級封鎖協議,會加 X 鎖,那麼就不能再加 S 鎖了,也就是不會讀入資料。
三級封鎖協議:在二級的基礎上,要求讀取資料 A 時必須加 S 鎖,直到事務結束了才能釋放 S 鎖。可以解決不可重複讀的問題,因為讀 A 時,其它事務不能對 A 加 X 鎖,從而避免了在讀的期間資料發生改變。
2. 兩段鎖協議
加鎖和解鎖分為兩個階段進行。
可序列化排程是指,通過併發控制,使得併發執行的事務結果與某個序列執行的事務結果相同。序列執行的事務互不干擾,不會出現併發一致性問題。
事務遵循兩段鎖協議是保證可序列化排程的充分條件。例如以下操作滿足兩段鎖協議,它是可序列化排程。
lock-x(A)...lock-s(B)...lock-s(C)...unlock(A)...unlock(C)...unlock(B)
但不是必要條件,例如以下操作不滿足兩段鎖協議,但它還是可序列化排程。
lock-x(A)...unlock(A)...lock-s(B)...unlock(B)...lock-s(C)...unlock(C)
多版本併發控制
Multi-Version Concurrency Control, MVCC,是 MySQL 的 InnoDB 儲存引擎實現隔離級別的一種具體方式,用於實現讀已提交和可重複讀這兩種隔離級別。而讀未提交隔離級別總是讀取最新的資料行,要求很低,無需使用 MVCC。可序列化隔離級別需要對所有讀取的行都加鎖,單純使用 MVCC 無法實現。
基本思想
-
實際場景中讀操作往往多於寫操作。讀和讀沒有互斥關係。讀和寫操作是互斥的。
-
MVCC 利用了多版本的思想,寫操作更新最新的版本快照,而讀操作去讀舊版本快照,沒有互斥關係。
-
在 MVCC 中事務的修改操作(DELETE、INSERT、UPDATE)會為資料行新增一個版本快照。
-
髒讀和不可重複讀原因:事務讀取到其它事務未提交的修改。MVCC 規定只能讀取已經提交的快照。
Undo日誌
MVCC 的多版本指的是多個版本的快照,快照儲存在 Undo 日誌中,該日誌通過回滾指標 ROLL_PTR 把一個數據行的所有快照連線起來。
快照中除了記錄事務版本號 TRX_ID 和操作之外,還記錄了一個 bit 的 DEL 欄位,用於標記是否被刪除。
ReadView
MVCC 維護了一個 ReadView 結構,主要包含了當前系統未提交的事務列表 TRX_IDs {TRX_ID_1, TRX_ID_2, …},還有該列表的最小值 TRX_ID_MIN 和 TRX_ID_MAX。
在進行 SELECT 操作時,根據資料行快照的 TRX_ID 與 TRX_ID_MIN 和 TRX_ID_MAX 之間的關係,從而判斷資料行快照是否可以使用:
- TRX_ID < TRX_ID_MIN,表示該資料行快照時在當前所有未提交事務之前進行更改的,因此可以使用。
- TRX_ID > TRX_ID_MAX,表示該資料行快照是在事務啟動之後被更改的,因此不可使用。
- TRX_ID_MIN <= TRX_ID <= TRX_ID_MAX,需要根據隔離級別再進行判斷:
- 提交讀:如果 TRX_ID 在 TRX_IDs 列表中,表示該資料行快照對應的事務還未提交,則該快照不可使用。否則表示已經提交,可以使用。
- 可重複讀:都不可以使用。因為如果可以使用的話,那麼其它事務也可以讀到這個資料行快照並進行修改,那麼當前事務再去讀這個資料行得到的值就會發生改變,也就是出現了不可重複讀問題。
在資料行快照不可使用的情況下,需要沿著 Undo Log 的回滾指標 ROLL_PTR 找到下一個快照,再進行上面的判斷。
快照讀與當前讀
1. 快照讀
MVCC 的 SELECT 操作是快照中的資料,不需要進行加鎖操作。
SELECT * FROM table ...;
2. 當前讀
MVCC 其它會對資料庫進行修改的操作(INSERT、UPDATE、DELETE)需要進行加鎖操作,從而讀取最新的資料。可以看到 MVCC 並不是完全不用加鎖,而只是避免了 SELECT 的加鎖操作。
INSERT;
UPDATE;
DELETE;
在進行 SELECT 操作時,可以強制指定進行加鎖操作。以下第一個語句需要加 S 鎖,第二個需要加 X 鎖。
SELECT * FROM table WHERE ? lock in share mode;
SELECT * FROM table WHERE ? for update;
檢視
- 將查詢sql封裝成一個虛擬表
- 虛擬表只保留sql邏輯,不儲存查詢結果
- 作用
- 封裝複雜sql語句,提高複用性
- 邏輯放到資料庫上,更新不需要釋出新程式,更靈活
- 適用於很多地方公用一組查詢結果。報表
- 建立修改檢視:create or replace view 檢視名 as select …
主從複製
- slave會從master讀取binlog來進行資料同步
- 三步驟
- master資料改變,並記錄到二進位制日誌binary log中。這個記錄過程稱為二進位制日誌事件binary log events
- slave的IO執行緒將master的二進位制日誌事件拷貝到它的中繼日誌relay log
- slave的SQL執行緒重做中繼日誌的事件,並將改變應用到自己的資料庫中。MySQL複製是非同步、序列化的。
- 複製的基本規則
- 每個slave只有一個master
- 每個slave只能有唯一一個伺服器ID
- 每個master可以有多個slave
- 一主一從配置
- 主從的MySQL版本一致,同一網段
- 配置到 [mysqld] 結點下,my.ini 和 my.cnf 配置檔案。
- 主機配置:
- [必選]:主伺服器唯一ID:server-id=1
- [必選]:啟用二進位制檔案:log-bin=路徑/data/mysqlbin
- [可選]:啟用錯誤日誌:log-err=路徑/data/mysqlerr
- [可選]:根目錄:basedir=路徑
- [可選]:臨時目錄:tmpdir=路徑
- [可選]:資料目錄:datadir=路徑/Data
- 讀寫都可以:read-only=0
- [可選]:不復制的資料庫:binlog-ignore-db=mysql
- [可選]:要複製的資料庫:binlog-do-db=資料庫
- binlog_format:
- statement:記錄所有寫操作sql。若sql存在函式,可能造成主從複製不一致
- row:記錄每一行的改變。若sql改變的行數特別多,那就每一行都要記錄。
- mixed:有函式用row,沒有函式用statement。系統變數無法複製。
- 從機配置:
- [必選]:從伺服器唯一ID:server-id=2
- [可選]:啟用二進位制日誌:log-bin=mysql-bin
- 重啟資料庫:service mysql stop。 service mysql start
- 主從機關閉防火牆:service iptables stop 。或開放埠
- 主機授權從機:
- grant replication slave on *.* to ‘使用者’@‘從機資料庫ip’ identified by ’密碼‘;
- 重新整理命令:flush privileges;
- 檢視主機狀態:show master status;# file:二進位制檔案,position:起始接入點。忽略資料庫、複製資料庫。
- 從機配置主機
- change master to master_host=’主機ip‘, master_user=‘使用者名稱’, master_password=‘密碼’, master_log_file=‘mysqlbin.數字’, master_log_pos=數字;
- 啟動:start slave;
- 檢視從機狀態:show slave status; # Slave_IO_Running 和 Slave_SQL_Running 必須同時為Yes。
- 停止:stop slave;
批量資料指令碼
- show variables like ‘log_bin_trust_function_creators’; # MySQL二進位制日誌,主從複製
- 在主從複製過程中,函式執行可能結果不一致,如當前時間函式,MySQL預設禁止使用函式。
- set global log_bin_trust_function_creators=1;#允許使用函式
- mysqld重啟,引數失效。永久方法,widows下的my.ini[mysqld]中加上,linux下 /etc/my.cnf下的my.cnf[mysqld]加上,log_bin_trust_function_creators=1
- 隨機生成字串函式
- 隨機生成數字函式
- 建立插入資料的儲存過程
隨機生成字串,n個字元
delimiter $$
create function rand_string(n int) returns varchar(255)
begin
declare chars_str varchar(100) default 'abcd...xyzABCD...XYZ';
declare return_str varchar(255) default '';
declare i int default 0;
while i<n do
set return_str = concat(return_str, substring(chars_str, floor(1+rand()*52), 1));
set i=i+1;
end while;
end $$
刪除函式: drop function rand_string
隨機生成數字,起始數字到結束
delimiter $$
create function rand_string(from_num int, to_num int) returns varchar(255)
begin
declare i int default 0;
set i=floor(from_num+rand()*(to_num-from_num+1));
return i;
end $$
建立插入資料的儲存過程,起始值,最大資料
delimiter $$
create procedure insert_emp(start_id int, max_num int)
begin
declare i int default 0;
set autocommit=0;
repeat
set i=i+1
insert into emp(emp_no, name, age, dept_id) values ((start_id+i), rand_string(6), rand(30, 50), rand_num(1, 10000)); # 隨機生成6位姓名,30-50歲,1-1w部門的員工。
until i=max_num
end repeat;
commit;
end $$
呼叫:
delimiter ;
call insrt_emp(1, 100000); # 建立十萬員工
批量刪除表的某些索引
查詢索引:show index from 表名;
取出索引名:在information_schema 的statistics表內:select index_name from information_schema.statistics where table_name=‘emp’ and index_name<>‘primary’ and seq_in_index=1;
刪除索引
delimiter $$
create procedure 'proc_drop_index'(dbname, varchar(200), tablename varchar(200))
begin
declare done int default 0;
declare ct int default 0;
declare _index varchar(200) default '';
declare _cur cursor for select index_name from information_schema.statistics where table_schema=dbname and table_name=tablename and index_name<>'primary' and seq_in_index=1; # 建立遊標
declare continue handler for not found set done=2;
open _cur;
fetch _cur into _index; # 取出遊標的一個值給index
while _index<>'' do # index不為空
set @str=concat('drop idnex', _index, "on", tablename); # 拼寫drop語句字串
prepare sql_str from @str; # 把字串預編譯為sql語句
execute sql_str; # 執行sql
deallocate prepare sql_str;
set _index='';
fetch _cur into _index;
end while;
close _cur;
end $$