深刻淺出MySQL數據庫開發、優化與管理維護(第2版)讀書筆記

基礎篇

SQL基礎

SQL分類

  1. DDL(Data Definition Languages):數據定義語句,這些語句定義了不一樣的數據段、數據庫、表、列、索引等數據庫對象。create、drop、alter等。
  2. DML(Data Manipulation Language):數據操縱語句,用於添加、刪除、更新和查詢數據庫記錄,並檢查數據完整性。insert、delete、update和select等。
  3. DCL(Data Control Language):數據控制語句,用於控制不一樣數據段直接的許可和訪問級別的語句。這些語句定義了數據庫、表、字段、用戶訪問權限和安全級別。grant、revoke等。

DDL語句

它和DML語句最大的區別是DML只是對錶內部數據操做,而不涉及表的定義、結構的修改,更不會涉及其餘對象。mysql

1. 建立數據庫
CREATE DATABASE dbname
複製代碼
2. 刪除數據庫
DROP DATABASE dbname
複製代碼
3. 建立表
CREATE TABLE tablename (
column_name_1 column_type_1 constraints,
column_name_2 column_type_2 constraints,
...
column_name_n column_type_n constraints)
複製代碼

MySQL的表名是以目錄的形式存在於磁盤上的,因此表名的字符能夠用任何目錄名容許的字符。算法

4.刪除表
DROP TALBE tablename
複製代碼
5. 修改表
-- 修改表類型
ALTER TABLE tablename MODIFY [COLUMN] column_definition [FIRST|AFTER col_name]
-- 增長表字段
ALTER TABLE tablename ADD [COLUMN] column_definition [FIRST|AFTER col_name]
-- 刪除表字段
ALTER TABLE tablename DROP [COLUMN] col_name
-- 字段更名
ALTER TALBE tablename CHANGE [COLUMN] old_col_name new_col_name column_definition [FIRST|AFTER col_name]
--  更改表名
ALTER TABLE tablename RENAME [TO] new_tablename
複製代碼

注意:sql

  1. change和modify均可以修改表的定義,不一樣的是change後面須要寫兩次列名,不方便。但change的優勢是能夠修改列名稱,modify不能。
  2. CHANGE/FIRST|AFTER COLUMN這些關鍵字都屬於MySQL在標準SQL上的擴展,在其餘數據庫上不必定適用。

DML語句

1. 插入記錄
INSERT INTO tablename(field1,field2,...,fieldn) VALUES(value1,value2,...,valuen)
複製代碼
2. 更新記錄
UPDATE tablename SET field1=value1,field2=value2,...,fieldn=valuen [WHERE CONDITION]
複製代碼
3. 刪除記錄
DELETE FROM tablename [WHERE CONDITION]
複製代碼

在MySQL中能夠一次刪除多個表的數據,語法以下:shell

DELETE t1,t2,...,tn FROM t1,t2,...,tn [WHERE CONDITION]
複製代碼

若是from後面的表名用別名,則delete後面也要用相應的別名,不然會提示語法錯誤。數據庫

4. 查詢記錄
-- 排序
SELETE * FROM tablename [WHERE CONDITION] [ORDER BY field1 [DESC|ASC], field2 [DESC|ASC],...,fieldn [DESC|ASC]]
-- 限制
SELECT ... [LIMIT offset_start,row_count]
-- 聚合
SELECT [field1,field2,...,fieldn] fun_name FROM tablename [WHERE where_condition] [GROUP BY field1,field2,...,fieldn] [WITH ROLLUP] [HAVING having_condition]
複製代碼

注意:緩存

  1. WITH ROLLUP是可選語法,代表是否對分類聚合後的結果再進行彙總。
  2. having和where的區別在於,having是對聚合後的結果進行條件的過濾,而where是在聚合前就對記錄進行過濾,若是邏輯容許,儘量使用where先過濾記錄,這樣由於結果集減少,將對聚合的效率大大提升,最後再根據邏輯看是否用having進行再過濾。

DCL語句

DCL語句主要是DBA用來管理系統中的對象權限時使用,通常的開發人員不多使用。如下經過例子說明。安全

-- 建立一個數據庫用戶z1,具備對sakila數據庫中全部表的SELECT/INSERT權限
GRANT select,insert ON sakila.* to 'z1'@'localhost' identified by '123';
-- 收回INSERT權限
REVOKE insert ON sakila.* FROM 'z1'@'localhost';
複製代碼

MySQL支持的數據類型

數值類型

整數類型 字節 最小值 最大值
TINYINT 1 有符號 -128
無符號 0
有符號 127
無符號 255
SMALLINT 2 有符號 -32768
無符號 0
有符號 32767
無符號 65535
MEDIUMINT 3 有符號 -8388608
無符號 0
有符號 8388607
無符號 1677215
INT、INTEGER 4 有符號 -2147483648
無符號 0
有符號 2147483647
無符號 4294967295
BIGINT 8 有符號 -9223372036854775808
無符號 0
有符號 9223372036854775807
無符號 18446744073709551615
浮點類型 字節 最小值 最大值
FLOAT 4 ±1.175494351E-38 ±3.402823466E+38
DOUBLE 8 ±2.2250738585072014E-308 ±1.7976931348623157E+308
位類型 字節 最小值 最大值
BIT(M) 1~8 BIT(1) BIT(64)
定點類型 字節 描述
DEC(M,D),
DECIMAL(M,D)
M+2 最大值範圍與DOUBLE相同,給定DECIMAL的有效取值範圍由M和D決定

對於整型數據,MySQL還支持在類型名稱後面的小括號內指定顯示寬度,例如int(5)表示當數值寬度小於5位的時候在數字前面填滿寬度,若是不顯示指定寬度則默認爲int(11)。通常配合zerofill使用,顧名思義,zerofill就是用"0"填充的意思,也就是在數字位數不夠的空間用字符"0"填充。bash

若是一列指定爲zerofill,則MySQL自動爲該列添加UNSIGNED屬性。服務器

日期時間類型

日期時間類型 字節 最小值 最大值
DATE 4 1000-01-01 9999-12-31
DATETIME 8 1000-01-01 00:00:00 9999-12-31 23:59:59
TIMESTAMP 4 19700101080001 2038年的某個時刻
TIME 3 -838:59:59 838:59:59
YEAR 1 1901 2155

TIMESTAMP有一個重要的特色,就是和時區相關。當插入日期時,會先轉換爲本地時區後存放;而從數據庫裏面取出來時,也一樣須要將日期轉換爲本地時區後顯示。session

字符串類型

字符串類型 字節 描述及存儲需求
CHAR(M) M M爲0~255之間的整數
VARCHAR(M) M爲0~65535之間的整數,值的長度爲+1字節
TINYBLOB 容許長度0~255字節,值的長度+1字節
BLOB 容許長度0~65535字節,值的長度+2字節
MEDIUMBLOB 容許長度0~167772150字節,值的長度+3字節
LONGBLOB 容許長度0~4394967295字節,值的長度+4字節
TINYTEXT 容許長度0~255字節,值的長度+2字節
TEXT 容許長度0~65535字節,值的長度+3字節
MEDIUMTEXT 容許長度0~167772150字節,值的長度+3字節
LONGTEXT 容許長度0~4394967295字節,值的長度+4字節
VARBINARY(M) 容許長度0~M字節的變長字節字符串,值的長度+1個字節
BINARY(M) M 容許長度0~M字節的變長字節字符串

在檢索的時候,CHAR列刪除了尾部的空格,而VARCHAR則保留這些空格。

枚舉類型

它的值範圍須要在建立表時經過枚舉方式顯式指定,對1~255個成員的枚舉須要1個字節存儲;對於255~65535個成員,須要2個字節存儲。最多容許有65535個成員。

SET類型

SET和ENUM類型很是相似,也是一個字符串對象,裏面能夠包含0~64個成員。

SET和ENUM除了存儲以外,最主要的區別在於SET類型一次能夠選取多個成員,而ENUM則只能選一個。

經常使用函數

字符串函數

函數 功能
CONCAT(S1,S2,...,Sn) 字符串鏈接
INSERT(str,x,y,instr) 將字符串str從第x位置開始,y個字符長的子串替換爲字符串instr
LOWER(str) 轉小寫
UPPER(str) 轉大寫
LEFT(str,x) 返回字符串str最左邊的x個字符
RIGHT(str,x) 返回字符串str最右邊的x個字符
LPAD(str,n,pad) 用字符串pad對str最左邊進行填充,知道長度爲n個字符長度
RPAD(str,n,pad) 用字符串pad對str最右邊進行填充,知道長度爲n個字符長度
LTRIM(str) 去掉字符串str左側的空格
RTRIM(str) 去掉字符串str右側的空格
REPEAT(str,x) 返回str重複x次的結果
REPLACE(str,a,b) 用字符串b替換字符串str中全部出現的字符串a
STRCMP(s1,s2) 比較字符串s1和s2
TRIM(str) 去掉字符串行尾和行頭的空格
SUBSTRING(str,x,y) 返回從字符串str x位置起y個字符長度的字串

數值函數

函數 功能
ABS(x) 絕對值
CEIL(x) 返回大於x的最小整數
FLOOR(x) 返回小於x的最大整數
MOD(x,y) 返回x/y的模
RAND() 返回0~1內的隨機值
ROUND(x,y) 返回參數x的四捨五入的有y位小數的值
TRUNCATE(x,y) 返回數字x截斷爲y位小數的結果

日期和時間函數

函數 功能
CURDATE() 返回當前日期
CURTIME() 返回當前時間
NOW() 返回當前的日期和時間
UNIX_TIMESTAMP(date) 返回日期date的UNIX時間戳
FROM_UNIXTIME 返回UNIX時間戳的日期值
WEEK(date) 返回日期date爲一年中的第幾周
YEAR(date) 返回日期date的年份
HOUR(time) 返回time的小時值
MINUTE(time) 返回time的分鐘值
MONTHNAME(date) 返回date的月份名
DATE_FORMAT(date,fmt) 返回按字符串fmt格式化日期date值
DATE_ADD(date,INTERVAL expr type) 返回一個日期或時間值加上一個時間間隔的時間值
DATEDIFF(expr,expr2) 返回起始時間expr和結束時間expr2之間的天數

流程函數

函數 功能
IF(value,t,f) 若是value爲真,返回t,不然返回f
IFNULL(value1,value2) 若是value1不爲空,則返回value1,不然返回value2
CASE WHEN [value1] THEN [result1]...ELSE [default] END 若是value1是真,返回result1,不然返回default
CASE [expr] WHEN [value1] THEN [result1]...ELSE [default] END 若是expr等於value1,返回result,不然返回default

經常使用其餘函數

函數 功能
DATABASE() 返回當前數據庫名
VAERSION() 當前數據庫版本
USER() 當前登陸用戶名
INET_ATON(IP) 返回IP地址的數字表示
INET_NTOA(mum) 返回數字表示的IP地址
PASSWORD(str) 返回字符串str的加密版本
MD5() 返回字符串str的MD5值

開發篇

表類型(存儲引擎)的選擇

存儲引擎概述

MySQL5.0支持的存儲引擎包括MyISAM、InnoDB、BDB、MEMORY、MERGE、EXAMPLE、NDB Cluster、ARCHIVE、CSV、BLACKHOLE、FEDERATED等,其中InnoDB和BDB提供事務安全表,其餘存儲引擎都是非事務安全表。

查看當前默認存儲引擎,可使用以下命令。

show varivales like 'table_type';
複製代碼

查詢當前數據庫支持的存儲引擎,可使用如下兩種方式。

SHOW ENGINES\G;
SHOW VARIABLES LIKE 'have%';
複製代碼

各類存儲引擎的特性

特色 MyISAM InnoDB MEMORY MERGE NDB
存儲限制 64TB 沒有
事務安全 支持
鎖機制 表鎖 行鎖 表鎖 表鎖 行鎖
B樹索引 支持 支持 支持 支持 支持
哈希索引 支持 支持
全文索引 支持
集羣索引 支持
數據緩存 支持 支持 支持
索引緩存 支持 支持 支持 支持 支持
數據可壓縮 支持
空間使用 N/A
內存使用 中等
批量插入的速度
支持外鍵 支持

MyISAM

MyISAM不支持事務、也不支持外鍵,其優點是訪問的速度快,對事務完整性沒有要求或者以SELECT、INSERT爲主的應用基本上均可以使用這個引擎來建立表。

每一個MyISAM在磁盤上存儲成3個文件,其文件名都和表名相同,但擴展名分別是:

  • .frm 存儲表定義
  • .MYD MYData,存儲數據
  • .MYI MYIdex,存儲索引

數據文件和索引文件能夠放置在不一樣的目錄,平均分佈IO,得到更快的速度。

InnoDB

InnoDB存儲引擎提供了具備提交、回滾和崩潰回覆能力的事務安全。

自動增加列

能夠經過ALTER TABLE *** AUTO_INCREMENT = n;語句強制設置自動增加列的初始值,默認從1開始,可是該強制的默認值是保留在內存中的,若是該值在使用以前數據庫從新啓動,那麼這個強制的默認值就會丟失,就須要在數據庫啓動之後從新設置。

對於InnoDB,自動增加列必須是索引。若是是組合索引,也必須是組合索引的第一列,可是對於MyISAM表,自動增加列能夠是組合索引的其餘列,這樣插入記錄後,自動增加列是按照組合索引的前面幾列進行排序後遞增的。

外鍵約束

MySQL支持外鍵的存儲引擎只有InnoDB,在建立外鍵的時候,要求父表必須有對應的索引,子表在建立外鍵的時候也會自動建立對應的索引。

CREATE TABLE country(
	country_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
	country VARCHAR(50) NOT NULL,
  last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (country_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE city(
	city_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
  city VARCHAR(50) NOT NULL,
  country_id SMALL UNSIGNED NOT NULL,
  last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (city_id),
  KEY idx_fk_country_id (country_id),
  CONSTRAINT 'fk_city_country' FOREIGN KEY (country_id) REFERENCES country(country_id) ON DELETE RESTRICT ON UPDATE CASCADE
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
複製代碼

能夠指定在刪除、更新父表時,對子表進行的相應操做,包括RESTRICT、CASCADE、SET NULL和NO ACTION。其中RESTRICT和NO ACTION相同,是指限制在子表有關聯記錄的狀況下父表不能更新;CASCADE表示父表在更新或者刪除時,更新或者刪除子表對應記錄;SET NULL則表示父表在更新或者刪除的時候,子表的對應字段被SET NULL。

存儲方式

InnoDB存儲表和索引有如下兩種方式。

  1. 使用共享表空間存儲,這種方式建立的表的表結構保存在.frm文件中,數據和索引保存在innodb_data_home_dir和innodb_data_file_path定義的表空間中,能夠是多個文件。
  2. 使用多表空間存儲,這種方式建立的表的表結構仍然保存在.frm文件中,可是每一個表的數據和索引單獨保存在.ibd中。若是是個分區表,則每一個分區對應單獨的.ibd文件,文件名是「表名+分區名」,能夠在建立分區的時候指定每一個分區的數據文件的位置,以此來將表的IO均勻分佈在多個磁盤上。

MEMORY

MEMORY存儲引擎使用存在於內存中的內容建立表。每一個MEMORY表只實際對應一個磁盤文件,格式是.frm。MEMORY類型的表訪問很是快,由於它的數據是放在內存中的,而且默認使用HASH索引,可是一旦服務關閉,表中的數據就會丟失掉.

MERGE

MERGE存儲引擎是一組MyISAM組合,這些MyISAM表必須結構徹底相同,MERGE表自己並無數據,對MERGE類型的表能夠進行查詢、更新、刪除操做,這些操做其實是對內部的MyISAM表進行的。

MERGE表在磁盤上保留兩個文件,文件名以表的名字開始,一個.frm文件存儲表定義,另外一個.MRG文件包含組合表的信息,包括MERGE表由哪些表組成、插入新的數據時的依據。

字符集

MySQL支持的字符集

查看全部可用的字符集的命令是show character set;

或者查看information_schema.character_set,可用顯示全部的字符集和該字符集默認的校對規則。

MySQL字符集的設置

MySQL的字符集和校對規則有4個級別的默認設置:服務器級、數據庫級、表級和字段級。

服務器字符集和校對規則
  • 能夠在my.cnf中設置

  • [mysqld]
    character-set-server=gbk
    複製代碼
  • 或者在啓動選項中指定

  • mysqld --character-set-server=gbk
    複製代碼
  • 或者在編譯時指定

  • shell> cmake . -DEFALUT_CHARSET=gbk
    複製代碼

    可使用show variables like 'character_set_server'命令查詢當前服務器的字符集和校對規則。

    show variables like 'character_set_server';
    show variables like 'collation_server';
    複製代碼
數據庫字符集和校對規則

數據庫的字符集和校對規則在建立數據庫的時候指定,也能夠再建立完數據庫後經過alter database命令進行修改。須要注意的是,若是數據庫裏已經存在數據,由於修改字符集並不能將已有的數據按照新的字符集進行存放,因此不能經過修改數據庫的字符集直接修改數據的內容。

要顯示當前數據庫的字符集和校對規則,可使用show variables like 'character_set_database'show variables like 'collation_database'命令查看。

show variables like 'character_set_database';
show variables like 'collation_database';
複製代碼
表字符集和校對規則

表的字符集和校對規則在建立表的時候指定,能夠經過alter table命令進行修改,一樣,若是表中已有記錄,修改字符集對原有的記錄並無影響,不會按照新的字符集進行存放。表的字段仍然使用原來的字符集。

要顯示錶的字符集和校對規則,可使用show create table命令查看。

show create table z1\G;
複製代碼
列字符集和校對規則

MySQL能夠定義列級別的字符集和校對規則,主要是針對相同的表不一樣字段須要使用不一樣的字符集的狀況,應該說通常遇到這種狀況的概率比較小,這只是MySQL提供給咱們一個靈活設置的手段。

鏈接字符集和校對規則

對於客戶端和服務器的交互操做,MySQL提供了3中不一樣的參數:character_set_client、character_set_connection和character_set_results,分別表明客戶端、鏈接和返回結果的字符集。一般狀況下,這3個字符集應該是相同的,才能夠確保用戶寫入的數據能夠正確地讀出。

一般狀況下,不會單個設置這3個參數,能夠經過如下命令:

SET NAMES ***
複製代碼

這個命令能夠同時修改這3個參數的值。使用這個方法修改鏈接的字符集和校對規則,須要應用每次鏈接數據庫後都執行這個命令。

另外一個更簡便的方法,是在my.cnf中設置如下語句:

[mysql]
default-character-set=gbk
複製代碼

字符集的修改步驟

(1) 導出表結構

  1. mysqldump -uroot -p --default-character-set=gbk -d databasename > create.sql
    複製代碼

    其中--default-character-set=gbk表示設置以什麼字符集鏈接,-d表示只導出表結構,不導出數據。

(2) 手工修改create.sql中表結構定義中的字符集爲新的字符集。

(3) 確保記錄再也不更新,導出全部記錄。

  1. msyqldump -uroot -p --quick --no-create-info --extended-insert --default-character-set=latin1 databasename > data.sql
    複製代碼
    • --quick:該選項用於轉儲大的表,它強制mysqldump從服務器一次一行地檢索表中的行而不是檢索全部行,並在輸出前將它緩存到內存中。
    • --extended-insert:使用包含幾個VALUES列表的多行INSERT語法,這樣使轉儲文件更小,重載文件時能夠加速插入。
    • --no-craete-info:不導出每一個轉儲表的CREATE TABLE語句。
    • --default-character-set=latin1:按照原有的字符集導出全部數據,這樣導出的文件中,全部中文都是可見的,不會保存成亂碼。

(4) 打開data.sql,將SET NAMES latin1修改爲SET NAMES gbk。

(5) 使用新的字符集建立新的數據庫

create database databasename default charset gbk;
複製代碼

(6) 建立表,執行create.sql

mysql -uroot -p databasename < craete.sql
複製代碼

(7) 導入數據,執行data.sql

mysql -uroot -p databasename < data.sql
複製代碼

索引的設計和使用

索引的概述

MyISAM和InnoDB存儲引擎的表默認建立的都是BTREE索引。MySQL目前還不支持函數索引,可是支持前綴索引,即對索引的前N個字符建立索引。前綴索引的長度跟存儲引擎相關,對於MyISAM存儲引擎的表,索引的前綴長度能夠達到1000字節長,而對於InnoDB存儲引擎的表,索引的前綴長度最長是767字節。請注意前綴的限制應以字節爲單位進行測量,而CREATE TABLE語句中的前綴長度解釋爲字符數。

建立索引的語法

CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name [USING index_type]
ON table_name (index_col_name,...)
index_col_name: col_name [(length)] [ASC|DESC]
複製代碼

索引的刪除語法爲

DROP IDNEX index_name ON tab_namne
複製代碼

設計索引的原則

  • 最適合索引的列是出如今WHERE子句中的列,或鏈接子句中指定的列,而不是出如今SELECT關鍵字後的選擇列表中的列。
  • 使用惟一索引。索引的列的基數越大,索引的效果越好。
  • 使用短索引。若是對字符串列進行索引,應該指定一個前綴長度,只要有可能就應該這樣作。較小的索引涉及的磁盤IO較少,較短的值比較起來更快。
  • 利用最左前綴。
  • 不要過分索引。
  • 對於InnoDB存儲引擎的表,記錄默認會按照必定的順序保存,若是有明肯定義的主鍵,則按照主鍵順序保存。若是沒有主鍵,可是有惟一索引,那麼就是按照惟一索引的順序保存。若是既沒有主鍵又沒有惟一索引,那麼表中會自動生成一個內部列,按照這個列的順序保存。按照主鍵或者內部列進行的訪問是最快的,因此InnoDB表儘可能本身指定主鍵。InnoDB表的普通索引都會保存主鍵的鍵值,因此主鍵要儘量選擇較短的數據類型,能夠有效地減小索引的磁盤佔用,提升索引的緩存效果。

BTREE索引和HASH索引

HASH索引有一些重要的特徵須要在使用的時候特別注意,以下所示。

  • 只用於使用=或者<=>操做符的等式比較。
  • 優化器不能使用HASH索引來加速ORDER BY操做
  • MySQL不能肯定在兩個值之間大約有多少行。若是將一個MyISAM表改成HASH索引的MEMORY表,會影響一些查詢的執行效率。
  • 只能使用整個關鍵字來搜索一行。

而對於BTREE索引,當使用>、<、>=、<=、BETWEEN、!=或者<>,或者LIKE 'pattern'(其中'pattern'不以通配符開始)操做符時,均可以使用相關列上的索引。

視圖

什麼是視圖

視圖(View)是一種虛擬存在的表,對於使用視圖的用戶來講基本上是透明的。

視圖操做

建立或者修改視圖

建立視圖須要有CREATE VIEW的權限,而且對於查詢涉及的列有SELECT權限。若是使用CREATE OR REPLACE或者ALTER修改視圖,那麼還須要該視圖的DROP權限。

-- 建立視圖
CREATE [OR REPLACE] [ALGORITHM = {UNDEFINED|MERGE|TEMPTABLE}]
VIEW view_name [(column_list)]
AS select_statement
[WITH[CASCADED|LOCAL]CHECK OPTION]
-- 修改視圖
ALTER [ALGORITHM={UNDEFINED|MERGE|TEMPTABLE}]
VIEW view_name [(column_list)]
AS select_statement
[WITH[CASCADED|LOCAL]CHECK OPTION]
複製代碼

視圖的可更改性和視圖中查詢的定義有關係,如下類型的視圖是不可更新的。

  • 包含如下關鍵字的SQL語句:聚合函數(SUM、MIN、MAX、COUNT等)、DISTINCT、GROUP BY、HAVING、UNION、UNION ALL
  • 常量視圖
  • SELECT中包含子查詢
  • JOIN
  • FROM一個不能更新的視圖
  • WHERE子句的子查詢引用了FROM子句中的表
刪除視圖

用戶能夠一次刪除一個或者多個視圖,前提是必須有該視圖的DROP權限。

DROP VIEW [IF EXISTS] view_name[,view_name]...[RESTRICT|CASCADE]
複製代碼
查看視圖

從MySQL5.1版本開始,使用SHOW TABLES命令的時候不只顯示錶的名字,同時也會顯示視圖的名字。

SHOW TABLES
複製代碼

一樣,在使用SHOW TABLES STATUS命令的時候,不但能夠顯示錶的信息,同時也能夠顯示視圖的信息。

SHOW TABLES STATUS [FROM db_name] [LIKE 'pattern']
複製代碼

存儲過程和函數

什麼是存儲過程和函數

存儲過程和函數是事先通過編譯並存儲在數據庫中的一段SQL語句的集合。

存儲過程和函數的區別在於函數必須有返回值,而存儲過程沒有,存儲過程的參數可使用IN、OUT、INOUT類型,而函數的參數只能是IN類型的。

事務控制和鎖定語句

LOCK TALBE 和 UNLOCK TABLE

LOCK TABLES 能夠鎖定用於當前線程的表。若是表被其餘線程鎖定,則當前線程會等待,直到能夠獲取全部鎖定位置。

UNLOCK TALBES能夠釋放當前線程得到的任何鎖定。當前線程執行另外一個LOCK TABLES時,或當服務器的鏈接被關閉時,全部由當前線程鎖定的表被隱含地解鎖。

LOCK TABLES
	tal_name [AS alias] {READ[LOCAL]|[LOW_PRIORITY] WRITE}
	[,tal_name [AS alias] {READ[LOCAL]|[LOW_PRIORITY] WRITE}]...
UNLOCK TABLES
複製代碼

事務控制

MySQL經過SET AUTOCOMMIT、START TRANSACTION、COMMIT和ROLLBACK等語句支持本地事務,具體語法以下:

START TRANSACTION | BEGIN [WORK]
COMMIT [WORK] [AND [NO] CHAIN][[NO] RELEASE]
ROLLBACK [WORK] [AND [NO] CHAIN][[NO] RELEASE]
SET AUTOCOMMIT={0|1}
複製代碼

默認狀況下,MySQL是自動提交(AutoCommit)的,若是須要經過明確的Commit喝Rollback來提交和回滾事務,那麼就須要經過明確的事務控制命令來開始事務,這是和Oracle事務管理明顯不一樣的地方。

  • START TRANSACTION或BEGIN語句能夠開始一項新的事務
  • COMMIT和ROLLBACK用來提交或者回滾事務
  • CHAIN和RELEASE子句分別用來定義在事務提交或者回滾以後的操做,CHAIN會當即啓動一個新事務,而且和剛纔的事務具備相同的隔離級別,RELEASE則會斷開和客戶端的鏈接。
  • SET AUTOCOMMIT能夠修改當前鏈接的提交方式,若是設置了SET AUTOCOMMIT=0,則設置以後的全部事務 0須要經過明確的命令進行提交或者回滾。

在鎖表期間,用 START TRANSACTION命令開始一個新事務,會形成一個隱含地UNLOCK TABLES被執行。

MySQL分區

分區概述

分區引入了分區鍵(partition key)的概念,分區鍵用來根據某個區間值(或者範圍值)、特定值列表或者HASH函數值執行數據的彙集,讓數據根據規則分佈在不一樣的分區中,讓一個大對象變成一些小對象。

能夠經過SHOW VARIABLES命令來肯定當前的MySQL是否支持分區

SHOW VARIABLES LIEK '%partition%'
複製代碼

分區類型

在MySQL5.1中可用的分區類型,主要有如下4種。

  • RANGE分區:基於一個給定連續區間範圍,把數據分配到不一樣的分區。
  • LIST分區:相似RANGE分區,區別在LIST分區是基於枚舉出的值列表分區,RANGE是基於給定的連續區間範圍分區。
  • HASH分區:基於給定的分區個數,把數據分配到不一樣的分區。
  • KEY分區:相似於HASH分區。

在MySQL5.1版本中,RANGE分區、LIST分區、HASH分區都要求分區鍵必須是INT類型,或者經過表達式返回INT類型。不管哪一種MySQL分區類型,不能使用主鍵/惟一鍵字段以外的其餘字段分區。

RANGE分區

CREATE TABLE emp(
	id INT NOT NULL,
	store_id INT NOT NULL
)PARTITION BY RANGE(store_id)(
	PARTITION p0 VALUES LESS THEN (10),
	PARTITION p1 VALUES LESS THEN (20),
	PARTITION p2 VALUES LESS THEN (30)
);
複製代碼

LIST分區

CREATE TABLE expenses(
	expense_date DATE NOT NULL,
	category INT
)PARTITION BY LIST(category)(
	PARTITION p0 VALUES IN(3, 5),
	PARTITION p1 VALUES IN(1, 10),
	PARTITION p2 VALUES IN(4, 9),
  PARTITION p3 VALUES IN(2),
  PARTITION p4 VALUES IN(6)
);
複製代碼

COLUMNS分區

Columns分區是MySQL5.5引入的分區類型,引入Columns分區解決了MySQL5.5版本以前RANGE分區和LIST分區只支持整型分區,從而致使須要額外的函數計算獲得整數或經過額外的轉換表來轉換爲整數再分區的問題。Columns分區能夠細分爲RANGE Columns分區和LIST Columns分區,RANGE Columns分區和LIST Columns分區都支持整數、日期時間、字符串三大數據類型。

對比RANGE分區和LIST分區,Columns分區的亮點除了支持數據類型增長以外,另外,一大亮點是Columns分區還支持多列分區。

CREATE TABLE r3(
	a INT,
	b INT
)PARTITION BY RANGE COLUMNS(a, b)(
	PARTITION p01 VALUES LESS THAN (0, 10),
	PARTITION p02 VALUES LESS THAN (10, 10),
	PARTITION p03 VALUES LESS THAN (10, 20),
	PARTITION p04 VALUES LESS THAN (10, 35),
	PARTITION p05 VALUES LESS THAN (10, MAXVALUE),
	PARTITION p06 VALUES LESS THAN (MAXVALUE, MAXVALUE)
);
複製代碼

HASH分區

HASH分區主要用來分散熱點讀,確保數據在預先肯定個數的分區中儘量平均分佈。

MySQL支持兩種HASH分區,常規HASH分區和線性HASH分區;常規HASH使用的是取模算法,線性HASH分區使用的是一個線性的2的冪的運算法則。

-- 常規HASH分區
CREATE TABLE emp(
	id INT NOT NULL,
	store_id INT NOT NULL
)PARTITION BY HASH(store_id) PARTITIONS 4;

-- 線性HASH
CREATE TABLE emp(
	id INT NOT NULL,
	store_id INT NOT NULL
)PARTITION BY LINEAR HASH(store_id) PARTITIONS 4;
複製代碼

KEY分區

按照Key進行分區很是相似於按照HASH進行分區,只不過HASH分區容許使用用戶自定義的表達式,而Key分區不容許使用用戶自定義的表達式,須要使用MySQL服務器提供的HASH函數;同時HASH分區只支持整數分區,而Key分區支持使用除BLOB或Text類型外其餘類型的列做爲分區鍵。

CREATE TABLE emp(
	id INT NOT NULL,
	store_id INT NOT NULL,
  job VARCHAR(30) NOT NULL
)PARTITION BY KEY (job) PARTITIONS 4;
複製代碼

建立Key分區表的時候,能夠不指定分區鍵,默認會首先選擇使用主鍵做爲分區鍵,在沒有主鍵的狀況,會選擇非空惟一鍵做爲分區鍵。

MySQL分區處理NULL值的方式

MySQL不由止在分區鍵值上使用NULL,分區鍵多是一個字段或者一個用戶定義的表達式。通常狀況下,MySQL的分區把NULL當作零值,或者是一個最小值進行處理。

注意:RANGE分區中,NULL值會被看成最小值來處理;LIST分區中,NULL值必須出如今枚舉列表中,不然不被接受;HASH/KEY分區中,NULL值會被看成零值來處理。

優化篇

SQL優化

優化SQL語句的通常步驟

  1. 經過 show status 命令瞭解各類SQL的執行頻率
    show [session|global] status
    複製代碼

    下面的命令顯示了當前session中全部統計參數的值:

    mysql> show status like 'Com_%';
    +-------------------------------------+-------+
    | Variable_name                       | Value |
    +-------------------------------------+-------+
    | Com_admin_commands                  | 0     |
    | Com_assign_to_keycache              | 0     |
    | Com_alter_db                        | 0     |
    | Com_alter_event                     | 0     |
    | Com_alter_function                  | 0     |
    | Com_alter_instance                  | 0     |
    | Com_alter_procedure                 | 0     |
    | Com_alter_resource_group            | 0     |
    | Com_alter_server                    | 0     |
    | Com_alter_table                     | 0     |
    | Com_alter_tablespace                | 0     |
    | Com_alter_user                      | 0     |
    | Com_alter_user_default_role         | 0     |
    | Com_analyze                         | 0     |
    | Com_begin                           | 0     |
    | Com_binlog                          | 0     |
    | Com_call_procedure                  | 0     |
    | Com_change_db                       | 1     |
    | Com_change_master                   | 0     |
    | Com_change_repl_filter              | 0     |
    | Com_check                           | 0     |
    ...
    複製代碼

    Com_xxx表示每一個xxx語句執行的次數,一般比較關心的是如下幾個統計參數:

    • Com_select:執行SELECT操做的次數,一次查詢只累加1.
    • Com_insert:執行INSERT操做的次數,對於批量插入的INSERT操做,只累加一次。
    • Com_update:執行UPDATE操做的次數。
    • Com_delete:執行DELETE操做的次數。

    上面這些參數對於全部存儲引擎的表操做都會進行累加。下面這幾個參數只是針對InnoDB存儲引擎的,累加的算法也略有不一樣。

    • Com_rows_read:SELECT查詢返回的行數。
    • Com_rows_inserted:執行INSERT操做插入的行數。
    • Com_rows_updated:執行UPDATE操做更新的行數。
    • Com_rows_deleted:執行DELETE操做刪除的行數。

    經過以上幾個參數,能夠很容易地瞭解當前數據庫是以插入更新爲主仍是以查詢操做爲主。

    對於事務型的應用,能夠經過Com_commitCom_rollback能夠了解事務和回滾的狀況。

    經過如下幾個參數能夠了解數據庫的基本狀況:

    • Connections:試圖鏈接MySQL服務器的次數。
    • Uptime:服務器工做時間
    • Slow_queries:慢查詢的次數。
  2. 定位執行效率較低的SQL語句
    • 經過慢查詢日誌定位那些執行效率較低的SQL語句,用--log-slow-queries[=file_name]選項啓動時,mysqld寫一個包含全部執行時間超過long_query_time秒的SQL語句的日誌文件。
    • 慢查詢日誌在查詢結束之後才記錄,因此在應用反映執行效率出現問題的時候查詢慢查詢日誌並不能定位問題,可使用show processlist命令查看當前MySQL在進行的線程,包括線程的狀態、是否鎖表等,能夠實時地查看SQL的執行狀況,同時對一些鎖表操做進行優化。
  3. 經過EXPLAIN分析低效SQL的執行計劃
    • select_type:表示SELECT的類型,常見的取值有SIMPLE(簡單表,即不使用錶鏈接或者子查詢)、PRIMARY(主查詢,即外層的查詢)、UNION(UNION中的第二個或者後面的查詢語句)、SUBQUERY(子查詢中的第一個SELECT)等。
    • table:輸出結果集的表
    • type:表示MySQL在表中找到所需行的方式,或者叫訪問類型
      • type = ALL,全表掃描,MySQL遍歷全表來找到匹配的行。
      • type = index,索引掃描,MySQL遍歷整個索引來找到匹配的行。
      • type = range,索引範圍掃描,常見於<、<=、>、>=、between等
      • type = ref,使用非惟一索引掃描或惟一索引的前綴掃描,返回匹配某個單獨值的記錄行。
      • type = eq_ref,相似ref,區別就在使用的索引是惟一索引,對於每一個索引鍵值,表中只有一條記錄匹配;簡單來講,就是多表鏈接中使用primary key或者unique index做爲關聯條件。
      • type = const/system,單表中最多有一個匹配行,查詢起來很是迅速,因此這個匹配行中的其餘列的值能夠被優化器在當前查詢中看成常量來處理,例如,根據主鍵primary key或者惟一索引unique index進行的查詢。
      • type = NULL,MySQL不用訪問表或者索引,就能直接獲得結果。
      • type還有其餘值,如ref_or_null(與ref相似,區別在於條件中包含對NULL的查詢)、index_range(索引合併優化)、unique_subquery(in的後面是一個查詢主鍵字段的子查詢)、index_subquery(與unique_subquery相似,區別在於in的後面是查詢非惟一索引字段的子查詢)
    • possible_keys:表示查詢時可能使用的索引。
    • key:表示實際使用的索引。
    • key_len:使用到的索引字段的長度
    • rows:掃描行的數量
    • Extra:執行狀況的說明和描述,包含不適合在其餘列中顯示可是對執行計劃很是重要的額外信息。

    MySQL4.1開始引入explain extended命令,經過explain extended加上show warnings,可以看到SQL真正被執行以前優化器作了哪些SQL改寫。

    MySQL5.1開始支持分區功能,同時explain命令也增長了對分區的支持。能夠經過explain partitions命令查看SQL所訪問的分區。

  4. 經過show profile分析SQL

    經過have_profiling參數,查看當前MySQL是否支持profile。

    select @@have_profiling;
    +------------------+
    | @@have_profiling |
    +------------------+
    | YES              |
    +------------------+
    複製代碼

    默認profiling是關閉的,能夠經過set語句在session級別開啓profiling:

    select @@profiling;
    +-------------+
    | @@profiling |
    +-------------+
    | 0           |
    +-------------+
    set profiling = 1;
    複製代碼

    經過show profiles語句,查看執行SQL的Query ID

    show profiles;
    +----------+----------+-----------------------------+
    | Query_ID | Duration | Query                       |
    +----------+----------+-----------------------------+
    | 1        | 8.7e-05  | SHOW WARNINGS               |
    | 2        | 0.000141 | select @@profiling          |
    | 3        | 6.8e-05  | SHOW WARNINGS               |
    | 4        | 0.000324 | select * from customer_part |
    | 5        | 5.2e-05  | SHOW WARNINGS               |
    | 6        | 4.4e-05  | SHOW WARNINGS               |
    | 7        | 0.000199 | select @@have_profiling     |
    | 8        | 7.5e-05  | SHOW WARNINGS               |
    +----------+----------+-----------------------------+
    複製代碼

    經過show profile for query {queryId}語句可以看到執行過程當中線程的每一個狀態和消耗時間

    show profile for query 4;
    +--------------------------------+----------+
    | Status                         | Duration |
    +--------------------------------+----------+
    | starting                       | 0.000077 |
    | Executing hook on transaction  | 0.000009 |
    | starting                       | 0.000008 |
    | checking permissions           | 0.000006 |
    | Opening tables                 | 0.000033 |
    | init                           | 0.000006 |
    | System lock                    | 0.000009 |
    | optimizing                     | 0.000005 |
    | statistics                     | 0.000013 |
    | preparing                      | 0.000014 |
    | executing                      | 0.000052 |
    | end                            | 0.000005 |
    | query end                      | 0.000003 |
    | waiting for handler commit     | 0.000008 |
    | closing tables                 | 0.000008 |
    | freeing items                  | 0.000024 |
    | cleaning up                    | 0.000044 |
    +--------------------------------+----------+
    複製代碼

    MySQL支持進一步選擇all、cpu、block io、context、switch、page faults等明細類型來查看MySQL在使用什麼資源上耗費了太高的時間

    show profile cpu for query 1;
    複製代碼
  5. 經過trace分析優化器如何選擇執行計劃

    MySQL5.6提供了對SQL跟蹤trace。

    使用方式:首先打開trace,設置格式爲JSON,設置trace最大可以使用的內存大小,避免解析過程當中由於默認內存太小而不可以完整顯示。

    SET OPTIMIZER_TRACE="enabled=on",END_MARKERS_IN_JSON=on;
    SET OPTIMIZER_TRACE_MAX_MEM_SIZE=1000000;
    複製代碼

    接下來執行想要trace的SQL語句

    select rental_id from rental where rental_date >= '2005-05-25 04:00:00' and rental_date <= '2005-05-25 05:00:00' and inventory_id = 5566;
    複製代碼

    最後檢查INFORMATION_SCHEMA.OPTIMIZER_TRACE就能夠知道MySQL是如何執行SQL的

    SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE\G;
    複製代碼

索引問題

索引的存儲分類

索引是在MySQL的存儲引擎層中實現的,而不是在服務器層實現的。MySQL目前提供如下4種索引:

  • B-Tree索引
  • HASH索引:只有Memory引擎支持
  • R-Tree索引(空間索引):空間索引是MyISAM的一個特殊索引類型,主要用於地理空間數據類型。
  • Full-text(全文索引):全文索引也是MyISAM的一個特殊索引類型。

前綴索引也有缺點,在排序Order By和分組Group By操做的時候沒法使用。

MySQL如何使用索引
MySQL中可以使用索引的典型場景
  1. 匹配全值(Match the full value),對索引中全部列都指定具體值,便是對索引中的全部列都有等值匹配的條件。
  2. 匹配值的範圍查詢(Match a range of values),對索引的值可以進行範圍查找。
  3. 匹配最左前綴(Match a leftmost prefix),僅僅使用索引中的最左邊列進行查找。
  4. 僅僅對索引進行查詢(Index only query),當查詢的列都在索引的字段中時,查詢的效率更高。
  5. 匹配列前綴(Match a column prefix),僅僅使用索引中的第一列,而且只包含索引第一列的開頭一部分進行查找。
  6. 可以實現索引匹配部分精確而其餘部分進行範圍匹配(Match one part exactly and match a range on another part)。
  7. 若是列名是索引,那麼使用column name is null就會使用索引(區別於Oracle)。
  8. MySQL5.6引入了Index Condition Pushdown(ICP)的特性,進一步優化了查詢。Pushdown表示操做下放,某些狀況下的條件過濾操做下放到存儲引擎。以前操做是經過索引回表獲取記錄後,再根據條件來過濾出最後的查詢結果;下放後,在索引返回指針的時候已通過濾掉了,這樣可以下降沒必要要的IO訪問。
存在索引不能使用索引的典型場景
  1. 以%開頭的LIKE查詢不能利用B-Tree索引
  2. 數據類型出現隱式轉換的時候也不會使用索引
  3. 複合索引的狀況下,假如查詢條件不包含索引列最左邊部分,即不知足最左原則Leftmost,是不會使用複合索引。
  4. 若是MySQL估計使用索引比全表掃描慢,則不使用索引。
  5. 使用or分割開的條件,若是or前的條件中的列有索引,然後面的列沒有索引,那麼涉及的索引都不會被用到。由於or後面的條件列中沒有索引,那麼後面的查詢確定要走全表掃描,在存在全表掃描的狀況下,就沒有必要多一次索引掃描增長IO訪問,一次全表掃描過濾條件就足夠了。
查看索引使用狀況

若是索引正在工做,Handler_read_key的值將很高,這個值表明了一個行被索引值讀的次數,很低的值代表增長索引獲得的性能改善不高,由於索引並不常用。

Handler_read_rnd_next的值高則意味着查詢運行低效,而且應該創建索引補救。這個值的含義是在數據文件中讀下一行的請求數。

show status like 'Handler_read%';
複製代碼
兩個簡單實用的優化方法
  1. 按期分析表和檢查表

    -- 分析表
    ANLYZE [LOCAL|NO_WRITE_TO_BINLOG] TALBE tbl_name[,tbl_name]...
    
    -- 檢查表
    CHECK TALBE tbl_name[,tbl_name]...[option]...option={QUICK|FAST|MEDIUM|EXTENDED|CHANGED}
    複製代碼

    在分析期間,使用一個讀取鎖定對錶進行鎖定。

  2. 按期優化表

    OPTIMIZE [LOCAL|NO_WRITE_TO_BINLOG] TABLE tbl_name[,tbl_name]...
    複製代碼

    若是已經刪除了表的一大部分,或者若是已經對含有可變長度行的表(含有VARCHAR、BLOB或TEXT列的表)進行了不少更改,則應實用OPTIMIZE TABLE命令來進行表優化。這個命令能夠將表中的空間碎片進行合併,而且能夠消除因爲刪除或者更新形成的表空間浪費,但OPTIMIZE TABLE命令只對MyISAM、BDB和InnoDB表起做用。

經常使用SQL的優化
大批量插入

當用load命令導入數據的時候,適當的設置能夠提升導入的速度。

對於MyISAM存儲引擎的表,能夠經過如下方式快速地導入大量的數據。

ALTER TALBE tbl_name DISABLE KEYS;
loading the data
ALTER TABLE tbl_name ENABLE KEYS;
複製代碼

DISABLE KEYS和ENABLE KEYS用來打開或者關閉MyISAM表非惟一索引的更新。這種方式是對MyISAM表進行數據導入時的優化措施。

有如下幾種方式提升InnoDB表的導入效率。

  1. 由於InnoDB類型的表是按照主鍵的順序保存的,因此將導入的數據按照主鍵的順序排列,能夠有效地提升導入數據的效率。
  2. 在導入數據前執行SET UNIQUE_CHECKS=0,關閉惟一性校驗,在導入結束後執行SET UNIQUE_CHECKS=1,恢復惟一性校驗,能夠提升導入的效率。
  3. 若是應用使用自動提交的方式,建議在導入前執行SET AUTOCOMMIT=0,關閉自動提交,導入結束後再執行SET AUTOCOMMIT=1,打開自動提交,也能夠提升導入的效率。
優化INSET語句

當進行數據INSERT的時候,能夠考慮採用如下幾種優化方式。

  1. 若是同時從同一客戶插入不少行,應儘可能使用多個值表的INSERT INTO tlb_name VALUES(), (), ..., ()語句,這種方式將大大縮減客戶端與數據庫之間的鏈接、關閉等消耗,使得效率比分開執行的單個INSERT語句快(在大部分狀況下,使用多個值表的INSERT語句能比單個INSERT語句快上好幾倍)。
  2. 若是從不一樣客戶插入不少行,能夠經過使用ISNERT DELAYED語句獲得更高的速度。DELAYED的含義是讓INSERT語句立刻執行,其實數據都被放在內存的隊列中,並無真正寫入磁盤,這比每條語句分別插入要快得多;LOW_PRIORITY恰好相反,在全部其餘用戶對錶的寫完成後才進行插入。
  3. 將索引文件和數據文件分在不一樣的磁盤上存放(利用建表中的選項)
  4. 若是進行批量插入,能夠經過增長bulk_insert_buffer_size變量值得方法來提升速度,可是,這隻能對MyISAM表使用。
  5. 當從一個文本文件裝載一個表時,使用LOAD DATA INFILE。這一般比使用不少INSERT語句快20倍。
優化ORDER BY語句

MySQL中有兩種排序方式

第一種經過有序索引順序掃描直接返回有序數據,這種方式在使用explain分析查詢的時候顯示爲Using index,不須要額外的排序,操做效率較高。

第二種是經過對返回數據進行排序,也就是一般說的FileSort排序,全部不是經過索引直接返回排序結果的排序都叫Filesort排序。Filesort並不表明經過磁盤文件進行排序,而只是說明進行了一個排序操做,至於排序操做是否使用了磁盤文件或臨時表等,則取決於MySQL服務器對排序參數的設置和須要排序數據的大小。

Filesort是經過相應的排序算法,將取得的數據在sort_buffer_size系統變量設置的內存排序區中進行排序,若是內存裝載不下,它就會將磁盤上的數據進行分塊,再對各個數據塊進行排序,而後將各個塊合併成有序的結果集。sort_buffer_size設置的排序區是每一個線程獨佔的,因此同一時刻,MySQL中存在多個sort_buffer_size排序區。

優化GROUP BY語句

若是查詢包括GROUP BY但用戶想要避免排序結果的消耗,則能夠指定ORDER BY NULL禁止排序。

優化嵌套查詢

在某些狀況下,子查詢能夠被更有效率的鏈接(JOIN)替代。

鏈接(JOIN)之因此更有效率一些,是由於MySQL不須要在內存中建立臨時表來完成這個邏輯上須要兩個步驟的查詢工做。

MySQL如何優化OR條件

對於含有OR的查詢子句,若是要利用索引,則OR之間的每一個條件都必須用到索引;若是沒有索引,則應該考慮增長索引。

優化分頁查詢
  1. 第一種優化思路

    在索引上完成排序分頁的操做,最後根據主鍵關聯回原表查詢所須要的其餘列內容。

  2. 第二種優化思路

    把LIMIT查詢轉換爲某個位置的查詢。例如,分頁的時候,增長last_page_record,用來記錄上一頁最後一行的id。

    注意,這樣把LIMIT m, n轉換成LIMIT n的查詢,只適合在排序字段不會出現重複值的特定環境下,可以減輕分頁翻頁的壓力;若是排序字段出現大量重複值,而仍進行這種優化,那麼分頁結果可能會丟失部分記錄,不適用這種方式優化。

適用SQL提示
  1. USE INDEX

    在查詢語句中表名的後面,添加USE INDEX來提供但願MySQL去參考的索引列表,就可讓MySQL再也不考慮其餘可用的索引。

    mysql> explain select count(*) from rental use index(idx_rental_date);
    複製代碼
  2. IGNORE INDEX

    讓MySQL忽略一個或者多個索引,則可使用IGNORE INDEX做爲HINT。

    mysql> explain select count(*) from rental ignore index(idx_rental_date);
    複製代碼
  3. FORCE INDEX

    強制MySQL使用一個特定的索引,可在查詢中使用FORCE INDEX做爲HINT。

    mysql> explain select * from rental force index(idx_fk_inventory_id) where inventory_id > 1;
    複製代碼

優化數據庫對象

優化表的數據類型

在MySQL中,可使用函數PROCEDURE ANALYSE()對當前應用的表進行分析,該函數能夠對數據表中列的數據類型提出優化建議。

SELECT * FROM tbl_name PROCEDURE ANALYSE();
-- 不要爲那些包含的值多於16個或者256個字節的ENUM類型提出建議
SELECT * FROM tbl_name PROCEDURE ANALYSE(16, 256);
複製代碼

經過拆分提升表的訪問效率

  1. 垂直拆分

    若是一張表中某些列經常使用,某些列不經常使用,可使用垂直拆分,另外,垂直拆分可使得數據行變小,一個數據頁就能存放更多的數據,在查詢時就會減小I/O次數。

    缺點是,須要管理冗餘列,查詢全部數據須要聯合(JOIN)操做。

  2. 水平拆分

    根據一列或多列數據的值把數據行放到兩個獨立的表中。

逆規範化

反規範的好處是下降鏈接操做的需求、下降外碼和索引的數目,還可能減小表的數目,相應帶來的問題是可能出現數據的完整性問題。加快查詢速度,但會下降修改速度。

經常使用的範規範技術有增長冗餘列、增長派生列、從新組表和分割表。

  • 增長冗餘列:指在多個表中具備相同的列,它經常使用來在查詢時避免鏈接操做。
  • 增長派生列:指增長的列來自其餘表中的數據,由其餘表中的數據通過計算生成。
  • 從新組表:指若是許多用戶須要查看兩個錶鏈接出來的結果數據,則把這兩個表從新組成一個表來減小鏈接而提升性能。
  • 分割表

另外,逆規範化技術須要維護數據的完整性。經常使用的方法是批處理維護、應用邏輯和觸發器。

使用中間表提升統計查詢速度

對於數據量較大的表,在其上進行統計查詢一般會效率很低,而且要考慮統計查詢是否會對在線的應用產生負面影響。一般在這種狀況下,使用中間表能夠提升統計查詢的效率。

鎖問題

MySQL鎖概述

MyISAM和MEMORY存儲引擎採用的是表級鎖(table-level locking);DBD存儲引擎採用的是頁面鎖(page-level locking),但也支持表級鎖;InnoDB存儲引擎既支持行級鎖(row-level locking),也支持表級鎖,但默認狀況下采用行級鎖。

MySQL這3種鎖的特性可大體概括以下:

  • 表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的機率最高,併發度最低。
  • 行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的機率最低,併發度也最高。
  • 頁面鎖:開銷和加鎖時間介於表鎖和行鎖之間;會出現死鎖;鎖定粒度介於表鎖和行鎖之間,併發度通常。

MyISAM表鎖

MyISAM存儲引擎只支持表鎖。

查詢表級鎖爭用狀況
mysql> show status like 'table%';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Table_locks_immediate      | 3     |
| Table_locks_waited         | 0     |
+----------------------------+-------+
複製代碼

若是Table_locks_waited的值比較高,則說明存在着較嚴重的表級鎖爭用狀況。

MySQL表級鎖的鎖模式

MySQL表級鎖有兩種模式:表共享讀鎖(Table Read Lock)和表獨佔寫鎖(Table Write Lock)。

如何加表鎖

MyISAM在執行查詢語句(SELECT)前,會自動給涉及的全部表加讀鎖,在執行更新操做(UPDATE、DELETE、INSERT等)前,會自動給涉及的表加寫鎖,這個過程並不須要用戶干預。

顯示加鎖與解鎖

-- 加讀鎖
lock table tbl_name read [local];
lock talbes tbl1_name read local, tbl2_name read local;
-- 寫鎖
lock table tbl_name write;
-- 解鎖
unlock tables;
複製代碼
  • local選項,其做用就是在知足MyISAM表併發插入條件的狀況下,容許其餘用戶在表尾併發插入記錄。
  • 在用LOCK TABLES給表顯式加鎖時,必須同時取得全部涉及表的鎖,而且MySQL不支持鎖升級。
  • 當用LOCK TABLES時,不只須要一次鎖定用到的全部表,並且,同一個表在SQL語句中出現多少次,就要經過與SQL語句中相同的別名鎖定多少次,不然也會出錯!
併發插入

MyISAM表也支持查詢和插入操做的併發進行。

MyISAM存儲引擎有一個系統變量concurrent_insert,專門用於控制其併發插入的行爲,其值分別能夠爲0、1或2。

  • 0:不容許併發插入。
  • 1:若是MyISAM表中沒有空洞(即表的中間沒有被刪除的行),MyISAM容許在一個進程讀表的同時,另外一個進程從尾部插入記錄。這也是MySQL的默認設置。
  • 2:不管MyISAM表中有沒有空洞,都容許在表尾併發插入記錄。
MyISAM的鎖調度

MyISAM存儲引擎的讀鎖和寫鎖都是互斥的,讀寫操做是串行的。那麼,一個進程請求某個MyISAM表的讀鎖,同時另外一個進程也請求同一個表的寫鎖,MySQL如何處理呢?答案是寫進程先得到鎖。不只如此,即便讀請求先到鎖等待隊列,寫請求後到,寫鎖也會插到讀鎖請求以前!這是由於MySQL認爲寫請求通常比讀請求更重要。這也是MyISAM表不太適合有大量更新操做和查詢操做應用的緣由,由於,大量的更新操做會形成查詢操做很難得到寫鎖,從而可能永遠阻塞。

能夠經過一些設置來調節MyISAM的調度行爲。

  • 經過指定啓動參數low-priority-updates,使MyISAM引擎默認給予讀請求以優先的權利。
  • 經過執行命令SET LOW_PRIORITY_UPDATES=1,使該鏈接發出的更新請求優先級下降。
  • 經過指定INSERT、UPDATE、DELETE語句的LOW_PRIORITY屬性,下降該語句的優先級。

另外,MySQL也提供了一種折中的辦法來調節讀寫衝突,即給系統參數max_write_lock_count設置一個合適的值,當一個表的讀鎖達到這個值後,MySQL就暫時將寫請求的優先級下降,給讀進程必定得到鎖的機會。

InnoDB鎖問題

InnoDB支持事務,採用了行級鎖。

事務

事務是由一組SQL語句組成的邏輯處理單元,事務具備4個屬性:原子性(Atomicity)、一致性(Consistent)、隔離性(Isolation)、持久性(Durable)。

併發事務帶來的問題

  • 更新丟失(Lost Update):當兩個或多個事務選擇同一行,而後基於最初選定的值更新該行時,因爲每一個事務都不知道其餘事務的存在,就會發生丟失更新問題。
  • 髒讀(Dirty Reads):一個事務正在對一條記錄作修改,在這個事務完成並提交以前,這個記錄的數據就處於不一致狀態;另外一個事務讀取到了這些「髒」數據,就會產生讀未提交的數據。
  • 不可重複讀(Non-Repeatable Reads):一個事務在讀取某些數據後的某個時間,再次讀取之前讀過的數據,卻發現其讀出的數據已經發生了改變或某些記錄已經被刪除了!這種現象就叫「不可重複讀」。
  • 幻讀(Phantom Reads):一個事務按照相同的查詢提交從新讀取之前檢索過的數據,卻發現其餘事務插入了知足其查詢條件的新數據,這種現象就稱爲「幻讀」。

事務隔離級別

數據庫實現事務隔離的方式,基本上可分爲如下兩種。

  • 在讀取數據前,對其加鎖,阻止其餘事務對數據進行修改。
  • 不加任何鎖,經過必定機制生成一個數據請求時間點的一致性數據快照(Snapshot),並用這個快照來提供必定級別(語句級或事務級)的一致性讀取。從用戶的角度來看,好像是數據庫能夠提供同一數據的多個版本,所以,這種技術叫作數據多版本併發控制(Multi Version Concurrency Control,簡稱MVCC或MCC),也常常成爲多版本數據庫。

4種隔離性比較

隔離級別\讀數據一致性及併發反作用 讀數據一致性 髒讀 不可重複讀 幻讀
未提交讀(Read uncommitted) 最低級別,只能保證不讀取物理上損壞的數據
已提交讀(Read committed) 語句級
可重複讀(Repeatable read) 事務級
可序列化(Serializable) 最高級別,事務級
獲取InnoDB行鎖爭用狀況

能夠經過檢查InnoDB_row_lock狀態變量來分析系統上的行鎖的爭奪狀況

mysql> show status like 'innodb_row_lock%';
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| Innodb_row_lock_current_waits | 0     |
| Innodb_row_lock_time          | 0     |
| Innodb_row_lock_time_avg      | 0     |
| Innodb_row_lock_time_max      | 0     |
| Innodb_row_lock_waits         | 0     |
+-------------------------------+-------+
複製代碼

若是鎖爭用狀況比較嚴重,InnoDB_row_lock_waits和InnoDB_row_lock_time_avg的值比較高,能夠經過查詢 infomation_schema 數據庫中相關的表來查看鎖狀況,或經過設置InnoDB Monitors來進一步觀察發生鎖衝突的表、數據行等,並分析鎖爭用的緣由。

InnoDB的行鎖模式及加鎖方法

InnoDB實現瞭如下兩種類型的行鎖。

  • 共享鎖(S):容許一個事務去讀一行,阻止其餘事務得到相同數據集的排他鎖。
  • 排他鎖(X):容許得到排他鎖的事務更新數據,阻止其餘事務取得相同的數據集的共享讀鎖和排他寫鎖。

另外,爲了容許行鎖和表鎖共存,實現多粒度鎖機制,InnoDB還有兩種內部使用的意向鎖(Intention Locks),這兩種意向鎖都是表鎖。

  • 意向共享鎖(IS):事務打算給數據行加行共享鎖,事務在給一個數據行加共享鎖前必須先取得該表的IS鎖。
  • 意向排他鎖(IX):事務打算給數據行加行排他鎖,事務在給一個數據行加排他鎖前必須先取得該表的IX鎖。

上述鎖模式的兼容狀況具體以下:

當前鎖模式\是否兼容\請求鎖模式 X IX S IS
X 衝突 衝突 衝突 衝突
IX 衝突 兼容 衝突 兼容
S 衝突 衝突 兼容 兼容
IS 衝突 兼容 兼容 兼容

意向鎖是InnoDB自動加的,不需用戶干預。對於UPDATE、DELETE和INSERT語句,InnoDB會自動給涉及數據集加排他鎖(X);對於普通SELECT語句,InnoDB不會加任何鎖;事務能夠經過如下語句顯示給記錄集加共享鎖或排他鎖。

  • 共享鎖(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE
  • 排他鎖(X):SELECT * FROM table_name WHERE ... FOR UPDATE

用 SELECT ... IN SHARE MODE得到共享鎖,主要用在須要數據依存關係時來確認某行記錄是否存在,並確保沒有人對這個記錄進行UPDATE或者DELETE操做。可是若是當前事務也須要對該記錄進行更新操做,則頗有可能形成死鎖,對於鎖定行記錄後須要進行更新操做的應用,應該使用SELECT ... FOR UPDATE方式得到排他鎖。

InnoDB行鎖實現方式

InnoDB行鎖是經過給索引上的索引項加鎖來實現的,若是沒有索引,InnoDB將經過隱藏的聚簇索引來對記錄加鎖。InnoDB行鎖分爲3種情形。

  • Record lock:對索引加鎖。
  • Gap lock:對索引之間的「間隙」、第一條記錄前的「間隙」或最後一條記錄的「間隙」加鎖。
  • Next-key lock:前兩種的組合,對記錄及其前面的間隙加鎖。

InnoDB這種行鎖實現特色意味着:若是不經過索引條件檢索數據,那麼InnoDB將對錶中的全部記錄加鎖,實際效果跟表鎖同樣。

注意如下幾點:

  1. 在不經過索引條件查詢時,InnoDB會鎖定表中的全部記錄。
  2. 因爲MySQL的行鎖是針對索引加的鎖,不是針對記錄加的鎖,因此雖然是訪問不一樣行的記錄,可是若是是使用相同的索引鍵,是會出現鎖衝突的。
  3. 當表有多個索引的時候,不一樣的事務可使用不一樣的索引鎖定不一樣的行,不管是使用主鍵索引、惟一索引或普通索引,InnoDB都會使用行鎖來對數據加鎖。
  4. 即使在條件中使用了索引字段,可是否使用索引來檢索數據是由MySQL經過判斷不一樣執行計劃的代價來決定的,若是MySQL不使用索引,這種狀況下InnoDB也會對全部記錄加鎖。所以,在分析鎖衝突時,別忘記了檢查SQL的執行計劃,以確認是否真正使用了索引。
Next-Key鎖

當咱們使用範圍條件而不是相等條件檢索數據,並請求共享或排他鎖時,InnoDB會給符合條件的已有數據記錄的索引項加鎖;對於鍵值在條件範圍內但不存在的記錄,叫作「間隙(GAP)」,InnoDB也會對這個「間隙」加鎖,這種鎖機制就是所謂的Next-Key鎖。

舉例來講,假如emp表中只有101條記錄,其empid的值分別是一、二、...、100、101,下面的SQL:

select * from emp where empid > 100 for update;
複製代碼

是一個範圍條件的檢索,InnoDB不只會對符合條件的empid值爲101的記錄加鎖,也會對empid大於101(這些記錄並不存在)的「間隙」加鎖。

InnoDB使用Next-Key鎖的目的,一方面是爲了防止幻讀,以知足相關隔離級別的要求;另外一方面,是爲了知足其恢復和複製的須要。

還要特別說明的是,InnoDB除了經過範圍條件加鎖時使用Next-Key鎖外,若是使用相等條件請求一個不存在的記錄加鎖,InnoDB也會使用Next-Key鎖!

恢復和複製的須要,對InnoDB鎖機制的影響

MySQL經過BINLOG記錄執行成功的INSERT、UPDATE、DELETE等更新數據的SQL語句,並由此實現MySQL數據庫的恢復和主從複製。MySQL 5.6 支持3種日誌格式,即基於語句的日誌格式SBL、基於行的日誌格式RBL和混合格式。還支持4種複製模式。

  1. 基於SQL語句的複製SBR:這也是MySQL最先支持的複製模式。
  2. 基於行數據的複製RBR:這是MySQL5.1之後開始支持的複製模式,主要優勢是支持對非安全SQL的複製。
  3. 混合複製模式:對安全的SQL語句採用基於SQL語句的複製模式,對於非安全的SQL語句採用居於行的複製模式。
  4. 使用全局事務ID(GTIDs)的複製:主要是解決主從自動同步一致性問題。

對基於語句日誌格式(SBL)的恢復和複製而言,因爲MySQL的BINLOG是按照事務提交的前後順序記錄的,所以要正確恢復或複製數據,就必須知足:在一個事務未提交前,其餘併發事務不能插入知足其鎖定條件的任何記錄,也就是不容許出現幻讀。這已經超過了ISO/ANSI SQL92「可重複讀」隔離級別的要求,其實是要求事務要串行化。這也是許多狀況下,InnoDB要用到Next-Key鎖的緣由,好比在用範圍條件更新記錄時,不管實在Read Commited或是Repeatable Read隔離級別下,InnoDB都要使用Next-Key鎖,但這並非隔離級別要求的。

ISNERT ... SELECT ... 和 CREATE TALBE ... SELECT ..語句,可能會阻止對源表的併發更新。若是查詢比較複雜,會形成嚴重的性能問題,讀者在應用中應儘可能避免使用。實際上,MySQL將這種SQL叫作不肯定(non-deterministic)的SQL,屬於「Unsafe SQL」,不推薦使用。

何時使用表鎖
  • 第一種狀況是:事務須要更新大部分或所有數據,表又比較大,若是使用默認的行鎖,不只這個事務執行效率低,並且可能形成其餘事務長時間鎖等待和鎖衝突,這種狀況下能夠考慮使用表鎖來提升該事務的執行速度。
  • 第二種狀況是:事務涉及多個表,比較複雜,極可能引發死鎖,形成大量事務回滾。這種狀況也能夠考慮一次性鎖定事務涉及的表,從而避免死鎖,減小數據庫因事務回滾帶來的開銷。

在InnoDB下,使用表鎖要注意如下兩點。

  • 使用LOCK TABLES雖然能夠給InnoDB叫表鎖,但必須說明的是,表鎖不是由InnoDB存儲引擎層管理的,而是由其上一層——MySQL Server負責的,僅當autocommit=0、innodb_table_locks=1(默認設置)時,InnoDB層才能自動識別涉及表鎖的死鎖;不然,InnoDB將沒法自動檢測並處理這種死鎖。
  • 在用LOCK TABLES對InnoDB表加鎖時要注意,要將AUTOCOMMIT設爲0,不然MySQL不會給表加鎖;事務結束前,不要用UNLOCK TABLES釋放表鎖,由於UNLOCK TABLES會隱含地提交事務;COMMIT或ROLLBACK並不能釋放用LOCK TABLES加的表鎖,必須用UNLOCK TABLES釋放表鎖。
關於死鎖

MyISAM表鎖是deadlock free的,這是由於MyISAM老是一次得到所需的所有鎖,要麼所有知足,要麼等待,所以不會出現死鎖。但在InnoDB中,除單個SQL組成的事務外,鎖是逐步得到的,這就決定了在InnoDB中發生死鎖是有可能的。

發生死鎖後,InnoDB通常都能自動檢測到,並使一個事務釋放鎖並回退,另外一個事務得到鎖,繼續完成事務。但在涉及外部鎖或涉及表鎖的狀況下,InnoDB並不能徹底自動檢測到死鎖,這須要經過設置鎖等待超時參數innodb_lock_wait_timeout來解決。須要說明的是,這個參數並非只用來解決死鎖問題,在併發訪問比較高的狀況下,若是大量事務因沒法當即得到所需的鎖而掛起,會佔用大量計算機資源,形成嚴重性能問題,設置拖垮數據庫。咱們經過設置合適的鎖等待超時閾值,能夠避免這種狀況發生。

避免死鎖的經常使用方法:

  1. 在應用中,若是不一樣的程序會併發存取多個表,應儘可能約定以相同的順序來訪問表,這樣能夠大大下降產生死鎖的機會。
  2. 在程序以批量方式處理數據的時候,若是事先對數據排序,保證每一個線程按固定的順序來處理記錄,也能夠大大下降出現死鎖的可能。
  3. 在事務中,若是須要更新記錄,應該直接申請足夠級別的鎖,即排他鎖,而不該該先申請共享鎖,更新時再申請排他鎖,由於當用戶申請排他鎖時,其餘事務可能又已經得到了相同記錄的共享鎖,從而形成鎖衝突,甚至死鎖。
  4. 在REPEATABLE-READ隔離級別下,若是兩個線程同時對相同條件記錄用SELECT ... FOR UPDATE加排他鎖,在沒有符合該條件記錄狀況下,兩個線程都會加鎖成功。程序發現記錄尚不存在,就試圖插入一條新記錄,若是兩個線程都這麼作,就會出現死鎖。將隔離級別改爲READ COMMITTED,就可避免問題。
  5. 當隔離級別爲READ COMMITTED時,若是兩個線程都先執行SELECT ... FOR UPDATE,判斷是否存在符合條件的記錄,若是沒有,就插入記錄。此時,只有一個線程能插入成功,另外一個線程會出現鎖等待,當第1個線程提交後,第2個線程會因主鍵重出錯,但雖然這個線程出錯了,卻會得到一個排他鎖!這時若是出現第3個線程又來申請排他鎖,也會出現死鎖。
相關文章
相關標籤/搜索