1. 程式人生 > 實用技巧 >mysql索引與儲存引擎

mysql索引與儲存引擎

2020年11月24日21:23:06-2020年11月28日19:41:00 by ddhhdd

01 mysql索引與儲存引擎

1.1 mysql的儲存引擎

(1)什麼是資料庫儲存引擎?

資料庫引擎是資料庫底層軟體元件,不同的儲存引擎提供不同的儲存機制,索引技巧,鎖定水平等功能,使用不同的資料庫引擎,可以獲得特定的功能。

(2)如何檢視引擎?

# 如何檢視資料庫支援的引擎
show engines;

# 檢視當前資料的引擎
show create table 表名\G

# 檢視當前庫所有表的引擎
show table status\G
# mysql的引擎可不止MyISAM和InnoDB
mysql> show engines;
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| Engine             | Support | Comment                                                        | Transactions | XA   | Savepoints |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| MEMORY             | YES     | Hash based, stored in memory, useful for temporary tables      | NO           | NO   | NO         |
| CSV                | YES     | CSV storage engine                                             | NO           | NO   | NO         |
| MRG_MYISAM         | YES     | Collection of identical MyISAM tables                          | NO           | NO   | NO         |
| BLACKHOLE          | YES     | /dev/null storage engine (anything you write to it disappears) | NO           | NO   | NO         |
| InnoDB             | DEFAULT | Supports transactions, row-level locking, and foreign keys     | YES          | YES  | YES        |
| PERFORMANCE_SCHEMA | YES     | Performance Schema                                             | NO           | NO   | NO         |
| ARCHIVE            | YES     | Archive storage engine                                         | NO           | NO   | NO         |
| MyISAM             | YES     | MyISAM storage engine                                          | NO           | NO   | NO         |
| FEDERATED          | NO      | Federated MySQL storage engine                                 | NULL         | NULL | NULL       |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
9 rows in set (0.00 sec)

(3)建表時指定引擎

create table yingqin (id int,name varchar(20)) engine='InnoDB';

(4)修改表的引擎

alter table 表名 engine='MyISAM';
# 修改預設引擎
•  vi /etc/my.cnf
•  [mysqld]下面
•  default-storage-engine=MyISAM
•  記得儲存後重啟服務

(5)MyISAM與InnoDB的區別

  • MyISAM:支援全文索引(full text);不支援事務;表級鎖;儲存表的具體行數;奔潰恢復不好;

  • Innodb:支援事務;以前的版本是不支援全文索引,但在5.6之後的版本就開始支援這個功能了;行級鎖(並非絕對,當執行sql語句時不能確定範圍時,也會進行鎖全表例如:update table set id=3 where name like 'a%';

    --行級鎖效率要遠高於表級鎖--ddh);不儲存表的具體行數;奔潰恢復好;

(6)總結:什麼時候選擇什麼引擎比較好

MyISAM:
• 一般來說MyISAM不需要用到事務的時候
• 做很多count計算

InnoDB:
• 可靠性要求高的,或者要求支援事務
• 想要用到外來鍵約束的時候(講外來鍵的時候會講)

推薦:
• 推薦用InnoDB

1.2 mysql索引

(1)什麼是索引?

索引是一個單獨的,儲存在磁碟中上的資料庫結構,它們包含著對資料表裡的所有記錄的引用指標。使用索引可以快速的找出在某列或多列中有特定值的行。

-- 索引相當於書的目錄,沒有索引,則需要遍歷整張表--ddh

(2)索引的優點

  • 通過建立唯一索引,來保證資料庫表中的每一行資料的唯一性。
  • 可以加快資料的檢索速度。
  • 可以保證表資料的完整性與準確性。

(3)索引的缺點

  • 索引需要佔用物理空間。
  • 對錶中的資料進行改動時,索引也需要跟著動態維護,降低了資料的維護速度。

(4)常見的索引型別

  • index:普通索引
  • unique:唯一索引
  • primary key:主鍵索引
  • foreign key:外來鍵索引
  • fulltext:全文索引
  • 組合索引

(5)建立表的sql語句

create table test (
  id int(7) zerofill auto_increment not null,
  username varchar(20),
  servnumber varchar(30),
  password varchar(20),
  createtime datetime,
  primary key (id)
)DEFAULT CHARSET=utf8;

(6)生成百萬甚至千萬級別表的sql語句shell指令碼

#!/bin/bash
echo "請輸入欄位servnumber的值:"
read serber
echo "請輸入建立sql語句的數量:"
read number
# char=`head /dev/urandom | tr -dc 0-9 | head -c 11`
for (( i=0;i<$number;i++ ))
        do
        pass=`head /dev/urandom | tr -dc a-z | head -c 8`
        let serber=serber+1
        echo "insert into test(id,username,servnumber,password,createtime)
        values('$i','user${i}','${serber}','$pass',now());" >>sql.txt
        done

具體操作:隨便在一個目錄下建立test.sh指令碼,我這裡以/usr/local/software目錄為例

cd /usr/local/software
# 編輯指令碼
vim test.sh
# 執行指令碼後會生成sql.txt
sh test.sh

# 在mysql中進行插入資料操作
mysql> source /usr/local/software/sql.txt
  • 建立好的test表如下:
mysql> select count(*) from test;
+----------+
| count(*) |
+----------+
|  3722351 |
+----------+
1 row in set (0.66 sec)

mysql> select * from test limit 5;

+---------+----------+-------------+----------+---------------------+
| id      | username | servnumber  | PASSWORD | createtime          |
+---------+----------+-------------+----------+---------------------+
| 0000001 | user0    | 13763946878 | xjrvoorp | 2020-11-24 22:53:31 |
| 0000002 | user2    | 13763946880 | atrsvjxo | 2020-11-24 22:53:31 |
| 0000003 | user3    | 13763946881 | dmsboqbn | 2020-11-24 22:53:31 |
| 0000004 | user4    | 13763946882 | nepqbpai | 2020-11-24 22:53:31 |
| 0000005 | user5    | 13763946883 | ersxlykr | 2020-11-24 22:53:31 |
+---------+----------+-------------+----------+---------------------+
5 rows in set (0.00 sec)

1.3 mysql普通索引與唯一索引

(1)什麼是普通索引?

普通索引(index):顧名思義就是各類索引中最為普通的索引,主要任務就是提高查詢速度。其特點是允許出現相同的索引內容,允許空(null)值。

(2)什麼是唯一索引?

唯一索引(unique):顧名思義就是不可以出現相同的索引內容,但是可以為空(null)值。

# 不能為createtime新增唯一索引,因為createtime不唯一
mysql> alter table test add unique unique_createtime (createtime );

ERROR 1062 (23000): Duplicate entry '2020-11-24 22:53:31' for key 'unique_createtime'

(3)如何建立普通索引或者唯一索引?

  • 建立表的時候建立
create table test (
  id int(7) zerofill auto_increment not null,
  username varchar(20),
  servnumber varchar(30),
  password varchar(20),
  createtime datetime,
  unique (id)
)DEFAULT CHARSET=utf8;
  • 直接為表新增索引
alter table 表名 add index 索引名稱 (欄位名稱);
# eg:alter table test add unique unique_username (username);

# 注意:假如沒有指定索引名稱時,會以預設的欄位名為索引名稱(欄位名就是索引名)
  • 直接建立索引
create index 索引 on 表名 (欄位名);
# eg:create index index_createtime on test (createtime);

(4)檢視索引

show index from 表名\G
# eg:show index from test\G
# 或
desc 表名;
# 或
show create table 表名\G;

(5)刪除索引

drop index 索引名稱 on 表名;
# eg:drop index unique_username on test;

alter table 表名 drop index 索引名;
# eg:alter table test drop index createtime;

(6)測試加索引前後的查詢速度

mysql> desc test;

+------------+--------------------------+------+-----+---------+----------------+
| Field      | Type                     | Null | Key | Default | Extra          |
+------------+--------------------------+------+-----+---------+----------------+
| id         | int(7) unsigned zerofill | NO   | PRI | NULL    | auto_increment |
| username   | varchar(20)              | YES  |     | NULL    |                |
| servnumber | varchar(30)              | YES  |     | NULL    |                |
| PASSWORD   | varchar(20)              | YES  |     | NULL    |                |
| createtime | datetime                 | YES  |     | NULL    |                |
+------------+--------------------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

mysql> select * from test order by id desc limit 2;

+---------+-------------+-------------+----------+---------------------+
| id      | username    | servnumber  | PASSWORD | createtime          |
+---------+-------------+-------------+----------+---------------------+
| 3722351 | user3722351 | 13767669229 | dtzwdhqc | 2020-11-25 01:00:40 |
| 3722350 | user3722350 | 13767669228 | swpddcdd | 2020-11-25 01:00:40 |
+---------+-------------+-------------+----------+---------------------+
2 rows in set (0.00 sec)

mysql> select * from test where password='dtzwdhqc';

+---------+-------------+-------------+----------+---------------------+
| id      | username    | servnumber  | PASSWORD | createtime          |
+---------+-------------+-------------+----------+---------------------+
| 3722351 | user3722351 | 13767669229 | dtzwdhqc | 2020-11-25 01:00:40 |
+---------+-------------+-------------+----------+---------------------+
1 row in set (1.49 sec)

mysql> select * from test where password='dtzwdhqc';

+---------+-------------+-------------+----------+---------------------+
| id      | username    | servnumber  | PASSWORD | createtime          |
+---------+-------------+-------------+----------+---------------------+
| 3722351 | user3722351 | 13767669229 | dtzwdhqc | 2020-11-25 01:00:40 |
+---------+-------------+-------------+----------+---------------------+
1 row in set (1.25 sec)

mysql> alter table test add index (password);
mysql> desc test;
+------------+--------------------------+------+-----+---------+----------------+
| Field      | Type                     | Null | Key | Default | Extra          |
+------------+--------------------------+------+-----+---------+----------------+
| id         | int(7) unsigned zerofill | NO   | PRI | NULL    | auto_increment |
| username   | varchar(20)              | YES  |     | NULL    |                |
| servnumber | varchar(30)              | YES  |     | NULL    |                |
| PASSWORD   | varchar(20)              | YES  | MUL | NULL    |                |
| createtime | datetime                 | YES  |     | NULL    |                |
+------------+--------------------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

mysql> select * from test where password='dtzwdhqc';

+---------+-------------+-------------+----------+---------------------+
| id      | username    | servnumber  | PASSWORD | createtime          |
+---------+-------------+-------------+----------+---------------------+
| 3722351 | user3722351 | 13767669229 | dtzwdhqc | 2020-11-25 01:00:40 |
+---------+-------------+-------------+----------+---------------------+
1 row in set (0.00 sec)

mysql> select * from test where password='swpddcdd';

+---------+-------------+-------------+----------+---------------------+
| id      | username    | servnumber  | PASSWORD | createtime          |
+---------+-------------+-------------+----------+---------------------+
| 3722350 | user3722350 | 13767669228 | swpddcdd | 2020-11-25 01:00:40 |
+---------+-------------+-------------+----------+---------------------+
1 row in set (0.01 sec)

1.4 mysql主鍵索引

(1)什麼是主鍵索引?

把主鍵新增索引就是主鍵索引,它是一種特殊的唯一索引,不允許有空值,而唯一索引(unique是允許為空值的)。指定為PRIMARY KEY

主鍵:主鍵是表的某一列,這一列的值是用來標誌表中的每一行資料的。

注意:每一張表只能擁有一個主鍵。

(2)建立主鍵索引

  • 建立表的時候建立
  • 直接為表新增主鍵索引
alter table 表名 add primary key (欄位名);
# eg:alter table test add primary key (id);

(3)刪除主鍵索引

 alter table 表名 drop primary key;

注意:刪除主鍵索引前需要先刪除auto_increment

# 刪除主鍵索引失敗
mysql> alter table test drop primary key;
ERROR 1075 (42000): Incorrect table definition; there can be only one auto column and it must be defined as a key

# 刪除主鍵索引前需要先刪除auto_increment
mysql> alter table test change id id int(7) unsigned zerofill not null;
Query OK, 3722351 rows affected (21.93 sec)
Records: 3722351  Duplicates: 0  Warnings: 0

mysql> desc test;
+------------+--------------------------+------+-----+---------+-------+
| Field      | Type                     | Null | Key | Default | Extra |
+------------+--------------------------+------+-----+---------+-------+
| id         | int(7) unsigned zerofill | NO   | PRI | NULL    |       |
| username   | varchar(20)              | YES  |     | NULL    |       |
| servnumber | varchar(30)              | YES  |     | NULL    |       |
| PASSWORD   | varchar(20)              | YES  |     | NULL    |       |
| createtime | datetime                 | YES  |     | NULL    |       |
+------------+--------------------------+------+-----+---------+-------+
5 rows in set (0.00 sec)

# 刪除主鍵索引成功
mysql> alter table test drop primary key;
Query OK, 3722351 rows affected (21.98 sec)
Records: 3722351  Duplicates: 0  Warnings: 0

mysql> desc test;
+------------+--------------------------+------+-----+---------+-------+
| Field      | Type                     | Null | Key | Default | Extra |
+------------+--------------------------+------+-----+---------+-------+
| id         | int(7) unsigned zerofill | NO   |     | NULL    |       |
| username   | varchar(20)              | YES  |     | NULL    |       |
| servnumber | varchar(30)              | YES  |     | NULL    |       |
| PASSWORD   | varchar(20)              | YES  |     | NULL    |       |
| createtime | datetime                 | YES  |     | NULL    |       |
+------------+--------------------------+------+-----+---------+-------+
5 rows in set (0.00 sec)

# 沒有主鍵索引查詢時間為(1.71 sec)
mysql> select * from test where id=3722351;
+---------+-------------+-------------+----------+---------------------+
| id      | username    | servnumber  | PASSWORD | createtime          |
+---------+-------------+-------------+----------+---------------------+
| 3722351 | user3722351 | 13767669229 | dtzwdhqc | 2020-11-25 01:00:40 |
+---------+-------------+-------------+----------+---------------------+
1 row in set (1.71 sec)

# 對比有主鍵索引的count(*),InnoDB引擎是怎麼執行查詢count(*)的?
mysql> select count(*) from test;
+----------+
| count(*) |
+----------+
|  3722351 |
+----------+
1 row in set (1.67 sec)

mysql> alter table test add primary key (id);
Query OK, 0 rows affected (30.55 sec)
Records: 0  Duplicates: 0  Warnings: 0

# 有主鍵索引查詢時間為(0.01 sec)
mysql> select * from test where id=3722351;
+---------+-------------+-------------+----------+---------------------+
| id      | username    | servnumber  | PASSWORD | createtime          |
+---------+-------------+-------------+----------+---------------------+
| 3722351 | user3722351 | 13767669229 | dtzwdhqc | 2020-11-25 01:00:40 |
+---------+-------------+-------------+----------+---------------------+
1 row in set (0.01 sec)

# 為什麼加了主鍵索引後會快一些?
mysql> select count(*) from test;
+----------+
| count(*) |
+----------+
|  3722351 |
+----------+
1 row in set (0.70 sec)

1.5 mysql全文索引

(1)什麼是全文索引?

全文索引是將儲存在資料庫中的文章或者句子等任意內容資訊查找出來的索引,單位是詞。全文索引也是目前搜尋引擎使用的一種關鍵技術。指定為fulltex。(MyISAM支援全文索引,InnoDB也支援啊?有啥區別?--ddh)

(區別是:1、在mysql5.6之前,InnoDB是不支援全文索引的。2、InnoDB不支援中文全文索引,即‘檢索內容’不能是中文,只能是英文--ddh 2020年11月27日00:26:03)

(2)建立練習表的sql

create table command (
  id int(5) unsigned primary key auto_increment,
  name varchar(10),
  instruction varchar(60)
)engine=MyISAM;
insert into command values('1','ls','list directory contents');
insert into command values('2','wc','print newline, word, and byte counts for each file');
insert into command values('3','cut','remove sections from each line of files');
insert into command values('4','sort','sort lines of text files');
insert into command values('5','find','search for files in a directory hierarchy');
insert into command values('6','cp','複製檔案或者資料夾');
insert into command values('7','top','display Linux processes');
insert into command values('8','mv','修改檔名,移動');
insert into command values('9','停止詞','is,not,me,yes,no ...');

(3)新增全文索引

  • 建立表的時候建立全文索引
  • 通過alter新增
alter table command add fulltext(instruction);

(4)使用全文索引

select * from 表名 where match (欄位名) against ('檢索內容');
# eg:select * from command where match(instruction) against ('sections');
  • 單位是詞的意思是:如果是英文,則填到‘檢索內容’中可以查出(如果有的話,停頓詞除外);如果是中文,則是以分割符為標準(比如逗號),填一個字或者多個字(不是兩個分割符之間的),則查不出來。

  • 檢視匹配度

select id, match(instruction) against ('directory') from command;
# 檢視匹配度
mysql> select id, match(instruction) against ('directory') from command;
+----+------------------------------------------+
| id | match(instruction) against ('directory') |
+----+------------------------------------------+
|  1 |                       1.2109839916229248 |
|  2 |                                        0 |
|  3 |                                        0 |
|  4 |                                        0 |
|  5 |                       1.1976701021194458 |
|  6 |                                        0 |
|  7 |                                        0 |
|  8 |                                        0 |
|  9 |                                        0 |
+----+------------------------------------------+
9 rows in set (0.00 sec)

# 查詢結果按匹配度排序
mysql> select * from command where match (instruction) against ('directory');
+----+------+-------------------------------------------+
| id | name | instruction                               |
+----+------+-------------------------------------------+
|  1 | ls   | list directory contents                   |
|  5 | find | search for files in a directory hierarchy |
+----+------+-------------------------------------------+
2 rows in set (0.00 sec)
  • 停止詞:出現頻率很高的詞,將會使全文索引失效
mysql> select * from command where match (instruction) against ('is');
Empty set (0.00 sec)

mysql> select * from command where match (instruction) against ('no');
Empty set (0.00 sec)
  • in boolean mode 模式
# in boolean mode:意思是指定全文檢索模式為布林全文檢索(簡單可以理解為是檢索方式)
select * from 表名 where match (欄位名) against ('檢索內容' in boolean mode);
# 查詢
mysql> select * from command where match (instruction) against ('directory');
+----+------+-------------------------------------------+
| id | name | instruction                               |
+----+------+-------------------------------------------+
|  1 | ls   | list directory contents                   |
|  5 | find | search for files in a directory hierarchy |
+----+------+-------------------------------------------+
2 rows in set (0.01 sec)

# 要以詞為單位
mysql> select * from command where match (instruction) against ('director*');
Empty set (0.00 sec)

# 相當於模糊查詢
mysql> select * from command where match (instruction) against ('director*' in boolean mode);
+----+------+-------------------------------------------+
| id | name | instruction                               |
+----+------+-------------------------------------------+
|  1 | ls   | list directory contents                   |
|  5 | find | search for files in a directory hierarchy |
+----+------+-------------------------------------------+
2 rows in set (0.00 sec)

mysql> select * from command where match (instruction) against ('*irectory' in boolean mode);
Empty set (0.00 sec)
  • 注意點:使用萬用字元*時,只能放在詞的後邊,不能放前邊。
# `加號`表示這兩個一定要出現才能匹配到
mysql> select * from command where match (instruction) against ('+directory +hierarchy' in boolean mode);
+----+------+-------------------------------------------+
| id | name | instruction                               |
+----+------+-------------------------------------------+
|  5 | find | search for files in a directory hierarchy |
+----+------+-------------------------------------------+
1 row in set (0.00 sec)

mysql> select * from command where match (instruction) against ('+directory+hierarchy' in boolean mode);
+----+------+-------------------------------------------+
| id | name | instruction                               |
+----+------+-------------------------------------------+
|  1 | ls   | list directory contents                   |
|  5 | find | search for files in a directory hierarchy |
+----+------+-------------------------------------------+
2 rows in set (0.00 sec)

# `加號`表示一定要出現,`不填`表示可以出現或者不出現
mysql> select * from command where match (instruction) against ('+directory hierarchy' in boolean mode);
+----+------+-------------------------------------------+
| id | name | instruction                               |
+----+------+-------------------------------------------+
|  1 | ls   | list directory contents                   |
|  5 | find | search for files in a directory hierarchy |
+----+------+-------------------------------------------+
2 rows in set (0.00 sec)

# `減號`表示一定不能出現
mysql> select * from command where match (instruction) against ('directory -contents' in boolean mode);
+----+------+-------------------------------------------+
| id | name | instruction                               |
+----+------+-------------------------------------------+
|  5 | find | search for files in a directory hierarchy |
+----+------+-------------------------------------------+
1 row in set (0.00 sec)

(5)刪除全文索引

alter table command drop index instruction;

(6)注意點總結

1、一般情況下建立全文索引的欄位資料型別為char、varchar、text 。其它欄位型別不可以。
2、全文索引不針對非常頻繁的詞做索引。比如is,no,not,you,me,yes這些,我們稱之為停止詞。
3、對英文檢索時忽略大小寫。

(7)測試

# 查詢最後兩條資料,用於測試
mysql> select * from test order by id desc limit 2;
+---------+-------------+-------------+----------+---------------------+
| id      | username    | servnumber  | PASSWORD | createtime          |
+---------+-------------+-------------+----------+---------------------+
| 3722351 | user3722351 | 13767669229 | dtzwdhqc | 2020-11-25 01:00:40 |
| 3722350 | user3722350 | 13767669228 | swpddcdd | 2020-11-25 01:00:40 |
+---------+-------------+-------------+----------+---------------------+
2 rows in set (2.00 sec)

# 建立全文索引
mysql> alter table test add fulltext(password);
Query OK, 0 rows affected, 1 warning (35.99 sec)
Records: 0  Duplicates: 0  Warnings: 1

# 查看錶中的所有索引
mysql> show index from test\G;
*************************** 1. row ***************************
        Table: test
   Non_unique: 1
     Key_name: PASSWORD
 Seq_in_index: 1
  Column_name: PASSWORD
    Collation: NULL
  Cardinality: 3704395
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: FULLTEXT
      Comment:
Index_comment:
1 row in set (0.00 sec)

# 不使用全文索引,用時(2.31 sec)
mysql> select * from test where password like '%dtzwdhqc%';
+---------+-------------+-------------+----------+---------------------+
| id      | username    | servnumber  | PASSWORD | createtime          |
+---------+-------------+-------------+----------+---------------------+
| 3722351 | user3722351 | 13767669229 | dtzwdhqc | 2020-11-25 01:00:40 |
+---------+-------------+-------------+----------+---------------------+
1 row in set (2.31 sec)

# 使用全文索引,用時(0.00 sec)
mysql> select * from test where match(password) against ('dtzwdhqc');
+---------+-------------+-------------+----------+---------------------+
| id      | username    | servnumber  | PASSWORD | createtime          |
+---------+-------------+-------------+----------+---------------------+
| 3722351 | user3722351 | 13767669229 | dtzwdhqc | 2020-11-25 01:00:40 |
+---------+-------------+-------------+----------+---------------------+
1 row in set (0.00 sec)

1.6 mysql外來鍵約束

(1)什麼是外來鍵?

外來鍵就是作用於兩個表資料之間的連結的一列或多列,用來保證表與表之間的資料的完整性和準確性。

  • 建立測試表sql
# 建立部門表
CREATE TABLE dept(
 deptnu  INT PRIMARY KEY comment '部門編號',
 dname  VARCHAR(50) comment '部門名稱',
 addr  VARCHAR(50) comment '部門地址'
);

# 某個公司的員工表
CREATE TABLE employee(
 empno  INT PRIMARY KEY comment '僱員編號',
 ename  VARCHAR(50) comment '僱員姓名',
 job  VARCHAR(50) comment '僱員職位',
 mgr  INT comment '僱員上級編號',
 hiredate  DATE comment '僱傭日期',
 sal  DECIMAL(7,2) comment '薪資',
 deptnu  INT comment '部門編號'
)ENGINE=MyISAM DEFAULT CHARSET=utf8;

# 建立工資等級表
CREATE TABLE salgrade(
 grade  INT PRIMARY KEY comment '等級',
 lowsal  INT comment '最低薪資',
 higsal  INT comment '最高薪資'
);

# 插入dept表資料
INSERT INTO dept VALUES (10, '研發部', '北京');
INSERT INTO dept VALUES (20, '工程部', '上海');
INSERT INTO dept VALUES (30, '銷售部', '廣州');
INSERT INTO dept VALUES (40, '財務部', '深圳');

# 插入emp表資料
INSERT INTO employee VALUES (1009, '唐僧', '董事長', NULL, '2010-11-17', 50000, 10);
INSERT INTO employee VALUES (1004, '豬八戒', '經理', 1009, '2001-04-02', 29750, 20);
INSERT INTO employee VALUES (1006, '猴子', '經理', 1009, '2011-05-01', 28500, 30);
INSERT INTO employee VALUES (1007, '張飛', '經理', 1009, '2011-09-01', 24500,10);
INSERT INTO employee VALUES (1008, '諸葛亮', '分析師', 1004, '2017-04-19', 30000, 20);
INSERT INTO employee VALUES (1013, '林俊杰', '分析師', 1004, '2011-12-03', 30000, 20);
INSERT INTO employee VALUES (1002, '牛魔王', '銷售員', 1006, '2018-02-20', 16000, 30);
INSERT INTO employee VALUES (1003, '程咬金', '銷售員', 1006, '2017-02-22', 12500, 30);
INSERT INTO employee VALUES (1005, '後裔', '銷售員', 1006, '2011-09-28', 12500, 30);
INSERT INTO employee VALUES (1010, '韓信', '銷售員', 1006, '2018-09-08', 15000,30);
INSERT INTO employee VALUES (1012, '安琪拉', '文員', 1006, '2011-12-03', 9500, 30);
INSERT INTO employee VALUES (1014, '甄姬', '文員', 1007, '2019-01-23', 7500, 10);
INSERT INTO employee VALUES (1011, '妲己', '文員', 1008, '2018-05-23', 11000, 20);
INSERT INTO employee VALUES (1001, '小喬', '文員', 1013, '2018-12-17', 8000, 20);

# 插入salgrade表資料
INSERT INTO salgrade VALUES (1, 7000, 12000);
INSERT INTO salgrade VALUES (2, 12010, 14000);
INSERT INTO salgrade VALUES (3, 14010, 20000);
INSERT INTO salgrade VALUES (4, 20010, 30000);
INSERT INTO salgrade VALUES (5, 30010, 99990);

(2)新增外來鍵約束

  • 建立表時新增
CREATE TABLE `employee` (
  `empno` int(11) NOT NULL COMMENT '僱員編號',
  `ename` varchar(50) DEFAULT NULL COMMENT '僱員姓名',
  `job` varchar(30) DEFAULT NULL,
  `mgr` int(11) DEFAULT NULL COMMENT '僱員上級編號',
  `hiredate` date DEFAULT NULL COMMENT '僱傭日期',
  `sal` decimal(7,2) DEFAULT NULL COMMENT '薪資',
  `deptnu` int(11) DEFAULT NULL COMMENT '部門編號',
  PRIMARY KEY (`empno`),
  foreign key (deptnu) references dept(deptnu)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

# 語法:foreign key (欄位名) references 關聯的表名(關聯表的欄位名)
# 注意:主鍵跟外來鍵的欄位型別一定要相同
  • alter table的方法
# 新增外來鍵約束:一個表的外來鍵的欄位--對應-->另一個表的主鍵的欄位
# 注意:這裡直接是KEY `deptnu` (`deptnu`),沒有`foreign`這個關鍵字,也沒有約束,是因為`ENGINE=MyISAM`
mysql> alter table employee add foreign key (deptnu) references dept(deptnu);

mysql> show create table employee\G;
*************************** 1. row ***************************
       Table: employee
Create Table: CREATE TABLE `employee` (
  `empno` int(11) NOT NULL COMMENT '僱員編號',
  `ename` varchar(50) DEFAULT NULL COMMENT '僱員姓名',
  `job` varchar(50) DEFAULT NULL COMMENT '僱員職位',
  `mgr` int(11) DEFAULT NULL COMMENT '僱員上級編號',
  `hiredate` date DEFAULT NULL COMMENT '僱傭日期',
  `sal` decimal(7,2) DEFAULT NULL COMMENT '薪資',
  `deptnu` int(11) DEFAULT NULL COMMENT '部門編號',
  PRIMARY KEY (`empno`),
  KEY `deptnu` (`deptnu`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

# 修改儲存引擎為InnoDB
mysql> alter table employee engine='InnoDB';
Query OK, 15 rows affected (0.02 sec)

# 新增外來鍵約束
mysql> alter table employee add foreign key (deptnu) references dept(deptnu);
Query OK, 14 rows affected (0.04 sec)

mysql> show create table employee\G;
*************************** 1. row ***************************
       Table: employee
Create Table: CREATE TABLE `employee` (
  `empno` int(11) NOT NULL COMMENT '僱員編號',
  `ename` varchar(50) DEFAULT NULL COMMENT '僱員姓名',
  `job` varchar(50) DEFAULT NULL COMMENT '僱員職位',
  `mgr` int(11) DEFAULT NULL COMMENT '僱員上級編號',
  `hiredate` date DEFAULT NULL COMMENT '僱傭日期',
  `sal` decimal(7,2) DEFAULT NULL COMMENT '薪資',
  `deptnu` int(11) DEFAULT NULL COMMENT '部門編號',
  PRIMARY KEY (`empno`),
  KEY `deptnu` (`deptnu`),
  CONSTRAINT `employee_ibfk_1` FOREIGN KEY (`deptnu`) REFERENCES `dept` (`deptnu`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

  • 保證資料的準確性與完整性
mysql> select * from dept;
+--------+-----------+--------+
| deptnu | dname     | addr   |
+--------+-----------+--------+
|     10 | 研發部    | 北京   |
|     20 | 工程部    | 上海   |
|     30 | 銷售部    | 廣州   |
|     40 | 財務部    | 深圳   |
+--------+-----------+--------+
4 rows in set (0.00 sec)

mysql> select * from employee;
+-------+-----------+-----------+------+------------+----------+--------+
| empno | ename     | job       | mgr  | hiredate   | sal      | deptnu |
+-------+-----------+-----------+------+------------+----------+--------+
|  1001 | 小喬      | 文員      | 1013 | 2018-12-17 |  8000.00 |     20 |
|  1002 | 牛魔王    | 銷售員    | 1006 | 2018-02-20 | 16000.00 |     30 |
|  1003 | 程咬金    | 銷售員    | 1006 | 2017-02-22 | 12500.00 |     30 |
|  1004 | 豬八戒    | 經理      | 1009 | 2001-04-02 | 29750.00 |     20 |
|  1005 | 後裔      | 銷售員    | 1006 | 2011-09-28 | 12500.00 |     30 |
|  1006 | 猴子      | 經理      | 1009 | 2011-05-01 | 28500.00 |     30 |
|  1007 | 張飛      | 經理      | 1009 | 2011-09-01 | 24500.00 |     10 |
|  1008 | 諸葛亮    | 分析師    | 1004 | 2017-04-19 | 30000.00 |     20 |
|  1009 | 唐僧      | 董事長    | NULL | 2010-11-17 | 50000.00 |     10 |
|  1010 | 韓信      | 銷售員    | 1006 | 2018-09-08 | 15000.00 |     30 |
|  1011 | 妲己      | 文員      | 1008 | 2018-05-23 | 11000.00 |     20 |
|  1012 | 安琪拉    | 文員      | 1006 | 2011-12-03 |  9500.00 |     30 |
|  1013 | 林俊杰    | 分析師    | 1004 | 2011-12-03 | 30000.00 |     20 |
|  1014 | 甄姬      | 文員      | 1007 | 2019-01-23 |  7500.00 |     10 |
+-------+-----------+-----------+------+------------+----------+--------+
14 rows in set (0.00 sec)

# 因為dept沒有50這個部門,所以在employee中也不能插入資料,這就保證資料的準確性與完整性
mysql> insert into employee values('2000','李信','經理','1009','2020-11-28','30000.00','50');
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`xdclass`.`employee`, CONSTRAINT `employee_ibfk_1` FOREIGN KEY (`deptnu`) REFERENCES `dept` (`deptnu`))

(3)刪除外來鍵約束

# 刪除外來鍵所以前需要先把外來鍵約束給刪除,不然會報錯
mysql> alter table employee drop index deptnu;
ERROR 1553 (HY000): Cannot drop index 'deptnu': needed in a foreign key constraint

# 刪除外來鍵約束
mysql> alter table employee drop index deptnu;
Query OK, 0 rows affected (0.01 sec)

# 刪除外來鍵索引
mysql> alter table employee drop index deptnu;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> show create table employee\G;
*************************** 1. row ***************************
       Table: employee
Create Table: CREATE TABLE `employee` (
  `empno` int(11) NOT NULL COMMENT '僱員編號',
  `ename` varchar(50) DEFAULT NULL COMMENT '僱員姓名',
  `job` varchar(50) DEFAULT NULL COMMENT '僱員職位',
  `mgr` int(11) DEFAULT NULL COMMENT '僱員上級編號',
  `hiredate` date DEFAULT NULL COMMENT '僱傭日期',
  `sal` decimal(7,2) DEFAULT NULL COMMENT '薪資',
  `deptnu` int(11) DEFAULT NULL COMMENT '部門編號',
  PRIMARY KEY (`empno`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

(4)注意點總結

  1. 兩個表,主鍵跟外來鍵的欄位型別一定要相同;
  2. 要使用外來鍵約束表的引擎一定得是InnoDB引擎,MyISAM是不起作用的;
  3. 在刪除外來鍵索引之前必須先把外來鍵約束刪除,才能刪除索引;

1.7 mysql聯合索引

(1)什麼是聯合索引?

聯合索引又稱組合索引或者複合索引,是建立在倆列或者多列以上的索引。

(2)建立聯合索引

alter table 表名 add index(欄位1,欄位2,欄位3);
# eg:alter table test add index(username,servnumber,password);
# 索引名預設為第一個欄位名
mysql> show create table test\G;
*************************** 1. row ***************************
       Table: test
Create Table: CREATE TABLE `test` (
  `id` int(7) unsigned zerofill NOT NULL,
  `username` varchar(20) DEFAULT NULL,
  `servnumber` varchar(30) DEFAULT NULL,
  `PASSWORD` varchar(20) DEFAULT NULL,
  `createtime` datetime DEFAULT NULL,
  KEY `username` (`username`,`servnumber`,`PASSWORD`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

(3)刪除聯合索引

alter table 表名 drop index 聯合索引名;

(4)為什麼要使用聯合索引,而不使用多個單列索引?

聯合索引的效率遠遠高於單列索引
# 建立了3個單列索引
mysql> show create table test\G;
*************************** 1. row ***************************
       Table: test
Create Table: CREATE TABLE `test` (
  `id` int(7) unsigned zerofill NOT NULL,
  `username` varchar(20) DEFAULT NULL,
  `servnumber` varchar(30) DEFAULT NULL,
  `PASSWORD` varchar(20) DEFAULT NULL,
  `createtime` datetime DEFAULT NULL,
  KEY `username` (`username`),
  KEY `PASSWORD` (`PASSWORD`),
  KEY `servnumber` (`servnumber`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql> select * from test where username like 'user3722%' and  servnumber like '1376766%' and  PASSWORD like 'dtzw%';
+---------+-------------+-------------+----------+---------------------+
| id      | username    | servnumber  | PASSWORD | createtime          |
+---------+-------------+-------------+----------+---------------------+
| 3722351 | user3722351 | 13767669229 | dtzwdhqc | 2020-11-25 01:00:40 |
+---------+-------------+-------------+----------+---------------------+
1 row in set (0.00 sec)

# 如果是單列索引,mysql會根據演算法選擇一個最優的索引進行查詢,通過這個索引鎖定一個範圍,在這個範圍內就不用索引了 
# possible_keys指可用的索引,key指該查詢使用的索引
mysql> explain select * from test where username like 'user3722%' and  servnumber like '1376766%' and  PASSWORD like 'dtzw%';
+----+-------------+-------+------------+-------+------------------------------+----------+---------+------+------+----------+------------------------------------+
| id | select_type | table | partitions | type  | possible_keys                | key      | key_len | ref  | rows | filtered | Extra                              |
+----+-------------+-------+------------+-------+------------------------------+----------+---------+------+------+----------+------------------------------------+
|  1 | SIMPLE      | test  | NULL       | range | username,PASSWORD,servnumber | PASSWORD | 63      | NULL |    6 |     0.83 | Using index condition; Using where |
+----+-------------+-------+------------+-------+------------------------------+----------+---------+------+------+----------+------------------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> select * from test where username like 'user3722%' and  servnumber like '1376766%' and  PASSWORD like 'd%';
+---------+-------------+-------------+----------+---------------------+
| id      | username    | servnumber  | PASSWORD | createtime          |
+---------+-------------+-------------+----------+---------------------+
| 3722045 | user3722045 | 13767668923 | diydylsx | 2020-11-25 01:00:39 |
| 3722056 | user3722056 | 13767668934 | denceouq | 2020-11-25 01:00:39 |
| 3722082 | user3722082 | 13767668960 | dzqpecuy | 2020-11-25 01:00:39 |
| 3722120 | user3722120 | 13767668998 | diabioph | 2020-11-25 01:00:39 |
| 3722135 | user3722135 | 13767669013 | dassrvvs | 2020-11-25 01:00:39 |
| 3722137 | user3722137 | 13767669015 | duvhxefb | 2020-11-25 01:00:39 |
| 3722142 | user3722142 | 13767669020 | duljpxgb | 2020-11-25 01:00:39 |
| 3722170 | user3722170 | 13767669048 | ddgoqjkq | 2020-11-25 01:00:39 |
| 3722236 | user3722236 | 13767669114 | dhdzwczi | 2020-11-25 01:00:39 |
| 3722304 | user3722304 | 13767669182 | dpczteei | 2020-11-25 01:00:39 |
| 3722318 | user3722318 | 13767669196 | dqozqloy | 2020-11-25 01:00:39 |
| 3722343 | user3722343 | 13767669221 | dbfyjhqp | 2020-11-25 01:00:40 |
| 3722351 | user3722351 | 13767669229 | dtzwdhqc | 2020-11-25 01:00:40 |
+---------+-------------+-------------+----------+---------------------+
13 rows in set (0.00 sec)

mysql> explain select * from test where username like 'user3722%' and  servnumber like '1376766%' and  PASSWORD like 'd%';
+----+-------------+-------+------------+-------+------------------------------+----------+---------+------+------+----------+------------------------------------+
| id | select_type | table | partitions | type  | possible_keys                | key      | key_len | ref  | rows | filtered | Extra                              |
+----+-------------+-------+------------+-------+------------------------------+----------+---------+------+------+----------+------------------------------------+
|  1 | SIMPLE      | test  | NULL       | range | username,PASSWORD,servnumber | username | 63      | NULL |  463 |     0.04 | Using index condition; Using where |
+----+-------------+-------+------------+-------+------------------------------+----------+---------+------+------+----------+------------------------------------+
1 row in set, 1 warning (0.00 sec)
  • 聯合索引的最左原則
# 建立聯合索引
mysql> alter table test add index composite_index (username,PASSWORD,servnumber);
Query OK, 0 rows affected (27.21 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> show create table test\G;
*************************** 1. row ***************************
       Table: test
Create Table: CREATE TABLE `test` (
  `id` int(7) unsigned zerofill NOT NULL,
  `username` varchar(20) DEFAULT NULL,
  `servnumber` varchar(30) DEFAULT NULL,
  `PASSWORD` varchar(20) DEFAULT NULL,
  `createtime` datetime DEFAULT NULL,
  KEY `username` (`username`),
  KEY `PASSWORD` (`PASSWORD`),
  KEY `servnumber` (`servnumber`),
  KEY `composite_index` (`username`,`PASSWORD`,`servnumber`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

# 聯合索引和單列索引都存在的情況下,並不一定走聯合索引
mysql> explain select * from test where username like 'user3722%' and  servnumber like '1376766%' and  PASSWORD like 'd%';
+----+-------------+-------+------------+-------+----------------------------------------------+----------+---------+------+------+----------+------------------------------------+
| id | select_type | table | partitions | type  | possible_keys                                | key      | key_len | ref  | rows | filtered | Extra                              |
+----+-------------+-------+------------+-------+----------------------------------------------+----------+---------+------+------+----------+------------------------------------+
|  1 | SIMPLE      | test  | NULL       | range | username,PASSWORD,servnumber,composite_index | username | 63      | NULL |  463 |     0.04 | Using index condition; Using where |
+----+-------------+-------+------------+-------+----------------------------------------------+----------+---------+------+------+----------+------------------------------------+
1 row in set, 1 warning (0.00 sec)

# 刪除單列索引
mysql> alter table test drop index username;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> alter table test drop index PASSWORD;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> alter table test drop index servnumber;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql>  show create table test\G;
*************************** 1. row ***************************
       Table: test
Create Table: CREATE TABLE `test` (
  `id` int(7) unsigned zerofill NOT NULL,
  `username` varchar(20) DEFAULT NULL,
  `servnumber` varchar(30) DEFAULT NULL,
  `PASSWORD` varchar(20) DEFAULT NULL,
  `createtime` datetime DEFAULT NULL,
  KEY `composite_index` (`username`,`PASSWORD`,`servnumber`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

# 走聯合索引
mysql> explain select * from test where username like 'user3722%' and  servnumber like '1376766%' and  PASSWORD like 'd%';
+----+-------------+-------+------------+-------+-----------------+-----------------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type  | possible_keys   | key             | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+-------+------------+-------+-----------------+-----------------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | test  | NULL       | range | composite_index | composite_index | 219     | NULL |  463 |     1.23 | Using index condition |
+----+-------------+-------+------------+-------+-----------------+-----------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

# 不符合最左原則,不走聯合索引
mysql> explain select * from test where servnumber like '1376766%' and  PASSWORD like 'd%';
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows    | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
|  1 | SIMPLE      | test  | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 3704395 |     1.23 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

(5)注意點總結

• 索引並非越多越好,過多的索引會增加資料的維護速度還有磁碟空間的浪費。
• 當表的資料量很大的時候,可以考慮建立索引。
• 表中經常查資料的欄位,可以考慮建立索引。
• 想要保證表中資料的唯一性,可以考慮建立唯一索引。
• 想要保證倆張表中的資料的完整性跟準確性,可以考慮建立外來鍵約束。
• 經常對多列資料進行查詢時,可以考慮建立聯合索引。

02 mysql的sql語句優化思路

2.1 mysql的慢查詢日誌開啟與問題定位

  • 第一步:檢視是否已經開啟了慢查詢日誌
mysql> show variables like 'slow%';
+---------------------+-----------------------------------------------------------------+
| Variable_name       | Value                                                           |
+---------------------+-----------------------------------------------------------------+
| slow_launch_time    | 2                                                               |
| slow_query_log      | OFF                                                             |
| slow_query_log_file | /usr/local/software/mysql/data/iZwz9bl327dijo79eslzmvZ-slow.log |
+---------------------+-----------------------------------------------------------------+
  • 第二步:開啟慢查詢日誌
mysql> set global slow_query_log = on ;
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like 'slow%';
+---------------------+-----------------------------------------------------------------+
| Variable_name       | Value                                                           |
+---------------------+-----------------------------------------------------------------+
| slow_launch_time    | 2                                                               |
| slow_query_log      | ON                                                              |
| slow_query_log_file | /usr/local/software/mysql/data/iZwz9bl327dijo79eslzmvZ-slow.log |
+---------------------+-----------------------------------------------------------------+
# 日誌路徑也可以自定義,慢查詢的sql都會記錄到裡面
set global slow_query_log_file = '路徑';
  • 第三步:檢視慢查詢的時間臨界值
# 查詢操作long_query_time:1s的sql都會記錄在裡面
mysql> show variables like '%long%';
+----------------------------------------------------------+----------+
| Variable_name                                            | Value    |
+----------------------------------------------------------+----------+
| long_query_time                                          | 1.000000 |
| performance_schema_events_stages_history_long_size       | 1000     |
| performance_schema_events_statements_history_long_size   | 1000     |
| performance_schema_events_transactions_history_long_size | 1000     |
| performance_schema_events_waits_history_long_size        | 1000     |
+----------------------------------------------------------+----------+
  • 第四步:設定慢查詢的時間標準
mysql> set long_query_time=0.4;
mysql> show variables like '%long%';
+----------------------------------------------------------+----------+
| Variable_name                                            | Value    |
+----------------------------------------------------------+----------+
| long_query_time                                          | 0.400000 |
| performance_schema_events_stages_history_long_size       | 1000     |
| performance_schema_events_statements_history_long_size   | 1000     |
| performance_schema_events_transactions_history_long_size | 1000     |
| performance_schema_events_waits_history_long_size        | 1000     |
+----------------------------------------------------------+----------+
  • 測試日誌記錄
# 沒有主鍵索引,則用id來測試
mysql> show create table test\G;
*************************** 1. row ***************************
       Table: test
Create Table: CREATE TABLE `test` (
  `id` int(7) unsigned zerofill NOT NULL,
  `username` varchar(20) DEFAULT NULL,
  `servnumber` varchar(30) DEFAULT NULL,
  `PASSWORD` varchar(20) DEFAULT NULL,
  `createtime` datetime DEFAULT NULL,
  KEY `composite_index` (`username`,`PASSWORD`,`servnumber`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql> select * from test where id=1000000;
+---------+-------------+-------------+----------+---------------------+
| id      | username    | servnumber  | PASSWORD | createtime          |
+---------+-------------+-------------+----------+---------------------+
| 1000000 | user1000000 | 13764946878 | jakonjlh | 2020-11-24 23:33:00 |
+---------+-------------+-------------+----------+---------------------+
1 row in set (2.00 sec)

[root@iZwz9bl327dijo79eslzmvZ ~]# tail -f /usr/local/software/mysql/data/iZwz9bl327dijo79eslzmvZ-slow.log
/usr/local/software/mysql/bin/mysqld, Version: 5.7.20 (MySQL Community Server (GPL)). started with:
Tcp port: 3306  Unix socket: /tmp/mysql.sock
Time                 Id Command    Argument
# Time: 2020-11-28T09:52:00.291662Z
# User@Host: root[root] @ localhost []  Id:   170
# Query_time: 1.982413  Lock_time: 0.000140 Rows_sent: 1  Rows_examined: 3722351
use xdclass;
SET timestamp=1606557120;
select * from test where id=1000000;
  • 注意:重啟mysql服務會讓在互動介面設定的慢查詢恢復到預設
# 永久生效的設定方法:修改配置檔案vim /etc/my.cnf
[mysqld]
slow_query_log = 1
long_query_time = 0.3
slow_query_log_file =/usr/local/software/mysql/mysql_slow.log
# 最後必須重啟服務才能生效!
service mysqld restart

2.2 mysql的sql語句執行過程解析

  • 第一步:檢視效能詳情是否開啟
mysql> show variables like '%profiling%';
+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| have_profiling         | YES   |
| profiling              | OFF   |
| profiling_history_size | 15    |
+------------------------+-------+
  • 第二步:開啟效能記錄功能
mysql> set profiling = on;
mysql> show variables like '%profiling%';
+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| have_profiling         | YES   |
| profiling              | ON    |
| profiling_history_size | 15    |
+------------------------+-------+
  • 第三步:檢視效能的記錄
# (0.00 sec)
mysql> select * from test where id=1000000;
+---------+-------------+-------------+----------+---------------------+
| id      | username    | servnumber  | PASSWORD | createtime          |
+---------+-------------+-------------+----------+---------------------+
| 1000000 | user1000000 | 13764946878 | jakonjlh | 2020-11-24 23:33:00 |
+---------+-------------+-------------+----------+---------------------+
1 row in set (0.00 sec)

#  (1.15 sec)
mysql> select * from test where username='user1000000';
+---------+-------------+-------------+----------+---------------------+
| id      | username    | servnumber  | PASSWORD | createtime          |
+---------+-------------+-------------+----------+---------------------+
| 1000000 | user1000000 | 13764946878 | jakonjlh | 2020-11-24 23:33:00 |
+---------+-------------+-------------+----------+---------------------+
1 row in set (1.15 sec)

# 開啟效能查詢後,所有的查詢記錄都會記錄在裡面
mysql> show profiles;
+----------+------------+-------------------------------------------------+
| Query_ID | Duration   | Query                                           |
+----------+------------+-------------------------------------------------+
|        1 | 1.15016025 | select * from test where username='user1000000' |
|        2 | 0.00024650 | select * from test where id=1000000             |
+----------+------------+-------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)
  • 第四步:檢視語句的執行效能詳情
# 檢視效能詳情
mysql> show profile for query 1;
+----------------------+----------+
| Status               | Duration |
+----------------------+----------+
| starting             | 0.000077 |
| checking permissions | 0.000006 |
| Opening tables       | 0.000015 |
| init                 | 0.000024 |
| System lock          | 0.000007 |
| optimizing           | 0.000008 |
| statistics           | 0.000014 |
| preparing            | 0.000011 |
| executing            | 0.000002 |
| Sending data         | 1.149891 |
| end                  | 0.000013 |
| query end            | 0.000010 |
| closing tables       | 0.000009 |
| freeing items        | 0.000027 |
| logging slow query   | 0.000038 |
| cleaning up          | 0.000012 |
+----------------------+----------+
16 rows in set, 1 warning (0.00 sec)

mysql> show profile cpu, block io for query 1;
+----------------------+----------+----------+------------+--------------+---------------+
| Status               | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out |
+----------------------+----------+----------+------------+--------------+---------------+
| starting             | 0.000077 | 0.000072 |   0.000000 |            0 |             0 |
| checking permissions | 0.000006 | 0.000006 |   0.000000 |            0 |             0 |
| Opening tables       | 0.000015 | 0.000014 |   0.000000 |            0 |             0 |
| init                 | 0.000024 | 0.000024 |   0.000000 |            0 |             0 |
| System lock          | 0.000007 | 0.000006 |   0.000000 |            0 |             0 |
| optimizing           | 0.000008 | 0.000009 |   0.000000 |            0 |             0 |
| statistics           | 0.000014 | 0.000013 |   0.000000 |            0 |             0 |
| preparing            | 0.000011 | 0.000011 |   0.000000 |            0 |             0 |
| executing            | 0.000002 | 0.000002 |   0.000000 |            0 |             0 |
| Sending data         | 1.149891 | 1.113559 |   0.037418 |            0 |             0 |
| end                  | 0.000013 | 0.000006 |   0.000001 |            0 |             0 |
| query end            | 0.000010 | 0.000009 |   0.000001 |            0 |             0 |
| closing tables       | 0.000009 | 0.000008 |   0.000001 |            0 |             0 |
| freeing items        | 0.000027 | 0.000023 |   0.000004 |            0 |             0 |
| logging slow query   | 0.000038 | 0.000033 |   0.000004 |            0 |             8 |
| cleaning up          | 0.000012 | 0.000010 |   0.000002 |            0 |             0 |
+----------------------+----------+----------+------------+--------------+---------------+
16 rows in set, 1 warning (0.00 sec)
  • 效能執行緒的詳細解釋官方文件連結:
https://dev.mysql.com/doc/refman/5.7/en/general-thread-states.html

2.3 mysql語句優化的幾個小建議

  • 儘量避免使用select *from,儘量精確到想要的結果欄位
  • 儘量避免條件使用or
  • 記得加上limit限制行數,避免資料量過大消耗效能
  • 使用模糊查詢時,%放在前面是會使索引失效
  • 要小心條件欄位型別的轉換