1. 程式人生 > 實用技巧 >Transforming the prediction target of sklearn

Transforming the prediction target of sklearn

資料庫三大設計正規化

  • 第一正規化 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:事務一旦結束提交,會永久的改變資料庫,即使出現系統故障也能保持。

事務隔離性:隔離級別越高,效能越差。

  1. 更新丟失,a、b同時修改一張表,只保留了最後一次結果。在一個事務未提交前,另一個事務不能訪問同一檔案。
  2. read uncommitted:讀未提交的——髒讀。b讀到a 已修改但未提交的資料。違反一致性
  3. read committed:讀已提交的——不可重複讀。b讀到a 已修改並提交的資料。導致b認為前後不一致。違反隔離性。
  4. repeatable read:可以重複讀——幻讀。預設級別。b讀到a 提交的新增資料。a插入資料,導致b兩次讀到的資料不同。b讀不到a已經提交的資料,導致插入操作出錯。違反隔離性。
  5. 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';

要求兩個語句必須同時成功或者同時失敗。

  1. mysql預設開啟事務(自動提交)select @@autocommit; – 1
  2. 開始事務時,執行sql語句後立即生效,不能回滾。 rollback;–無效
  3. 關閉事務:set autocommit=0;
  4. 關閉事務時,執行sql語句後,需要手動提交才不能回滾。commit;
  5. 在沒有commit前,rollback會回滾所有語句。所以轉賬時,可以兩條語句都執行完畢後再commit。

**手動開啟事務:**在最開始,輸入begin; 或者 start transaction;此後可以rollback;

邏輯架構

應用程式——連線池——快取(讀資訊)和緩衝(寫資訊)——讀不到資訊,到sql介面——解析器——優化器,生成執行計劃——儲存引擎——返回客戶端,並在快取存一份。

MySQL執行

啟動快取:

  • 修改配置檔案: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的區別:

MyISAMInnoDB
外來鍵不支援支援
事務不支援支援
行表鎖表鎖,即使操作一條記錄也會鎖住整個表,不適合高併發行鎖,操作時只鎖住某一行,不對其他行有影響,適合高併發(會出現死鎖)
快取只快取索引,不快取真實資料快取索引和真實資料,對記憶體要求高,記憶體大小對效能有決定性影響
關注節省資源、消耗少、簡單業務併發寫、事務、更大資源
預設安裝
預設使用
系統表

詳細版:

MyISAMInnodb
儲存結構每張表被存放在三個檔案:frm-表格定義、MYD(MYData)-資料檔案、MYI(MYIndex)-索引檔案所有的表都儲存在同一個資料檔案中(也可能是多個檔案,或者是獨立的表空間檔案),InnoDB表的大小隻受限於作業系統檔案的大小,一般為2GB
儲存空間MyISAM可被壓縮,儲存空間較小InnoDB的表需要更多的記憶體和儲存,它會在主記憶體中建立其專用的緩衝池用於高速緩衝資料和索引
可移植性、備份及恢復由於MyISAM的資料是以檔案的形式儲存,所以在跨平臺的資料轉移中會很方便。在備份和恢復時可單獨針對某個表進行操作免費的方案可以是拷貝資料檔案、備份 binlog,或者用 mysqldump,在資料量達到幾十G的時候就相對痛苦了
檔案格式資料和索引是分別儲存的,資料.MYD,索引.MYI資料和索引是集中儲存的,.ibd
記錄儲存順序按記錄插入順序儲存按主鍵大小有序插入
外來鍵不支援支援
事務不支援支援
鎖支援(鎖是避免資源爭用的一個機制,MySQL鎖對使用者幾乎是透明的)表級鎖定行級鎖定、表級鎖定,鎖定力度小併發能力高
SELECTMyISAM更優
INSERT、UPDATE、DELETEInnoDB更優
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次數更低。
  • 聚簇索引(資料行和相鄰鍵值聚簇的儲存在一起)和非聚簇索引

B樹

索引分類:

  • 單值:一個索引只包含單個列,可以有多個
  • 唯一:索引列的值必須唯一,允許空值。可以有多個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 欄位,用於標記是否被刪除。

img

ReadView

MVCC 維護了一個 ReadView 結構,主要包含了當前系統未提交的事務列表 TRX_IDs {TRX_ID_1, TRX_ID_2, …},還有該列表的最小值 TRX_ID_MIN 和 TRX_ID_MAX。

img

在進行 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 $$