1. 程式人生 > 其它 >【LeetCode】357. Count Numbers with Unique Digits 計算各個位數不同的數字個數(Medium)(JAVA)

【LeetCode】357. Count Numbers with Unique Digits 計算各個位數不同的數字個數(Medium)(JAVA)

MySQL體系結構與管理

一 體系結構

1.1 C/S(客戶端/服務端)模型介紹

image-20200812171707690

1
2
3
4
TCP/IP方式(遠端、本地):
mysql -uroot -poldboy123 -h 10.0.0.51 -P3306
Socket方式(僅本地):
mysql -uroot -poldboy123 -S /tmp/mysql.sock

1.2 例項介紹

image-20200812172639836

1
2
例項=mysqld後臺守護程序+Master Thread +幹活的Thread+預分配的記憶體
公司=老闆+經理+員工+辦公室

1.3 mysqld程式執行原理

1.3.1 mysqld程式結構

1
# cd /app/database/mysql/bin 路徑下的mysqld程式

image-20200812171734246

1.3.2 一條SQL語句的執行過程

1.3.2.1 連線層

1
2
3
4
5
6

(1)提供連線協議:TCP/IP 、SOCKET
(2)提供驗證:使用者、密碼,IP,SOCKET
(3)提供專用連線執行緒:接收使用者SQL,返回結果
通過以下語句可以檢視到連線執行緒基本情況
mysql> show processlist;

1.3.2.2 SQL層 (重點)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(1)接收上層傳送的SQL語句
(2)語法驗證模組:驗證語句語法,是否滿足SQL_MODE
(3)語義檢查:判斷SQL語句的型別
DDL :資料定義語言
DCL :資料控制語言
DML :資料操作語言
DQL: 資料查詢語言
...
(4)許可權檢查:使用者對庫表有沒有許可權
(5)解析器:對語句執行前,進行預處理,生成解析樹(執行計劃),說白了就是生成多種執行方案.
(6)優化器:根據解析器得出的多種執行計劃,進行判斷,選擇最優的執行計劃
代價模型:資源(CPU IO MEM)的耗損評估效能好壞
(7)執行器:根據最優執行計劃,執行SQL語句,產生執行結果
執行結果:在磁碟的xxxx位置上
(8)提供查詢快取(預設是沒開啟的),一般不用,會使用redis替代查詢快取功能
(9)提供日誌記錄(日誌管理章節):binlog,預設是沒開啟的。

1.3.2.3 儲存引擎層(類似於Linux中的檔案系統)

1
2
3
負責根據SQL層執行的結果,從磁碟上拿資料。
將16進位制的磁碟資料,交由SQL結構化化成表,
連線層的專用執行緒返回給使用者。

image-20200812175045993

1.4 邏輯結構

image-20200812171804012

1.4.1 庫(類似於目錄)

1
2
3
庫名,庫屬性
# show databases;
# use mysql

1.4.2 表(類似於檔案)

1
2
3
4
5
6
表名
屬性
列:列名(欄位),列屬性(資料型別,約束等)
資料行(記錄)
# show tables;
# desc user; # 查看錶列的情況

1.5 物理儲存結構引入

image-20200812171823222

1.5.1 庫的物理儲存結構

1
用檔案系統的目錄來儲存

1.5.2 表的物理儲存結構

1
2
3
4
5
6
7
8
9
10
MyISAM(一種引擎)的表:
-rw-r----- 1 mysql mysql 10816 Apr 18 11:37 user.frm
-rw-r----- 1 mysql mysql 396 Apr 18 12:20 user.MYD
-rw-r----- 1 mysql mysql 4096 Apr 18 14:48 user.MYI

InnoDB(預設的儲存引擎)的表:
-rw-r----- 1 mysql mysql 8636 Apr 18 11:37 time_zone.frm
-rw-r----- 1 mysql mysql 98304 Apr 18 11:37 time_zone.ibd
time_zone.frm:儲存列相關資訊
time_zone.ibd:資料行+索引

1.5.3 表的段、區、頁(16k)(瞭解)

1
2
3
段:一個表就是一個段,可以由一個或者多個區構成
區/簇:一個區(簇),預設1M,連續的64個頁(pages)
頁:一個頁,預設16k,連續的4個os的block,最小的儲存單元

image-20200812181944720

二 基礎管理

2.1 使用者、許可權管理

2.1.1 使用者

作用:

1
登入,管理資料庫邏輯物件

定義:

1
2
3
4
5
6
7
8
9
使用者名稱@'白名單'
白名單支援的方式?
wordpress@'10.0.0.%' # wordpress使用者可以通過10.0.0地址段的ip登陸
wordpress@'%' # wordpress使用者可以通過所有ip登陸
wordpress@'10.0.0.200' # wordpress使用者只能通過10.0.0.200ip登陸
wordpress@'localhost'# 本地
wordpress@'db02' # 通過主機名db02登陸
wordpress@'10.0.0.5%' #wordpress使用者可以通過50--59的ip登陸
wordpress@'10.0.0.0/255.255.254.0' # 地址段

管理操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
# mysql庫下user表,儲存使用者和授權資訊
# 增:
create user lqz@'localhost';
create user lqz@'%' identified by '123';
# 查:
desc mysql.user; ----> authentication_string
select user ,host ,authentication_string from mysql.user
# 改:
alter user lqz@'%' identified by '456';
# 刪:
drop user lqz@'%';

# 注意:8.0以前,可以通過grant命令,建立使用者+授權,8.0以後不再支援,必須先建使用者設定密碼,再授權

2.1.2 許可權

針對使用者設定許可權,許可權是使用者的屬性

許可權管理操作:

1
2
3
4
5
# 8.0以前
# grant 許可權 on 物件 to 使用者 identified by '密碼';
# 8.0以後
# create user 使用者 identified by '密碼';
# grant 許可權 on 物件 to 使用者;

常用許可權介紹:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
show privileges; # 檢視所有許可權
ALL:
SELECT,INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, CRE ATE TABLESPACE
ALL : 以上所有許可權,一般是普通管理員擁有的
with grant option:超級管理員才具備的,給別的使用者授權的功能

###################許可權:
ALL
SELECT,INSERT,UPDATE,DELETE
grant option
## 例子
grant all on wordpress.* to wordpress@'10.0.0.%' identified by '123';
grant SELECT,INSERT,UPDATE,DELETE on wordpress.* to wordpress@'10.0.0.%' identified by '123';
# all許可權不包含grant option,需要單獨設定。給其他使用者授權
grant all on wordpress.* to wordpress@'10.0.0.%' with grant option;

許可權作用範圍:

1
2
3
4
# 物件:庫,表
*.* # 所有庫,所有表(管理員)
lqz.* # lqz庫下的所有表(用的多)
lqz.article # lqz庫下的article表

需求1:windows機器的navicat登入到linux中的MySQL,管理員使用者。

1
mysql> grant all on *.* to root@'10.0.0.%' identified by '123';

需求2:建立一個應用使用者app使用者,能從windows上登入mysql,並能操作app庫

1
mysql> grant select ,update,insert,delete on app.* to app@'10.0.0.%' identified by '123';

2.1.3 開發人員使用者授權流程

1
2
3
4
1.許可權
2.對誰操作
3.你從哪來
4.密碼要求

2.1.4 提示:8.0在grant命令新增新特性

1
2
3
建使用者和授權分開了
grant 不再支援自動建立使用者了,不支援改密碼
授權之前,必須要提前建立使用者。

2.1.5 檢視授權

1
2
3
4
5
6
7
8
9
10
11
show grants for app@'10.0.0.%';

# 檢視使用者基本資訊
select * from mysql.user\G; # N和Y表示

# mysql授權表mysql庫下,每次資料庫啟動,會把資料載入到記憶體中
user: *.*範圍,存放建立的使用者密碼包括全域性例項級別管理許可權
db: 庫級別範圍 lqz.*
tables_priv: 表級別範圍 lqz.article
columns_priv: 列範圍,欄位級別
procs_priv: 存放儲存過程的許可權

2.1.6 回收許可權

1
2
3
4
# 使用者刪了,許可權也就沒了
# oracle中刪除使用者,使用者的表也沒了,mysql刪除使用者只是許可權沒了,表和庫還在
# 不能通過重複授權覆蓋之前的許可權,進行修改,只能回收許可權,只回收某個許可權,多次grants是疊加許可權
revoke delete on app.* from app@'10.0.0.%';

2.1.7 本地管理員使用者密碼忘記.

1
2
3
4
5
6
7
8
9
10
11
12
13
# mysqld啟動時,跳過授權表,跳過tcp/ip連線
--skip-grant-tables 跳過授權表
--skip-networking 跳過tcp/ip連線,不讓遠端使用者登入,否則遠端使用者不用密碼可以直接登入
#### ? service mysqld start --skip-grant-tables --skip-networking



[root@db01 ~]mysqld_safe --skip-grant-tables --skip-networking &
# 手工把授權表載入到記憶體
mysql> flush privileges;
mysql> alter user root@'localhost' identified by '123456';
[root@db01 ~]# pkill mysqld
[root@db01 ~]# systemctl start mysqld

2.2 連線管理

image-20200812200157223

2.2.1 自帶客戶端命令

mysql 常用引數:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
-u                   使用者
-p 密碼
-h IP
-P 埠
-S socket檔案
-e 免互動執行命令,做自動化運維,查詢資料庫使用者,建立使用者等等
< 匯入SQL指令碼

[root@db01 ~]# mysql -uroot -p -h 10.0.0.51 -P3306
Enter password:
mysql> select @@socket;
+-----------------+
| @@socket |
+-----------------+
| /tmp/mysql.sock |
# 資料庫中必須先授權 root@'localhost' 使用者
[root@db01 ~]# mysql -uroot -p -S /tmp/mysql.sock
Enter password:
[root@db01 ~]# mysql -uroot -p -e "select user,host from mysql.user;"
Enter password:
+---------------+-----------+
| user | host |
+---------------+-----------+
| root | 10.0.0.% |
| mysql.session | localhost |
| mysql.sys | localhost |
| root | localhost |
+---------------+-----------+
[root@db01 ~]#
[root@db01 ~]# mysql -uroot -p <world.sql
Enter password:


# 檢視客戶端連線情況,區分哪個是遠端連線過來,哪個是本地連線過來
show processlist;

2.3 多種啟動方式介紹

image-20200812171903857image-20200812211358893

提示:

1
2
3
4
5
6
以上多種方式,都可以單獨啟動MySQL服務
mysqld_safe和mysqld一般是在臨時維護時使用。
另外,從Centos 7系統開始,支援systemd直接呼叫mysqld的方式進行啟動資料庫

方式三四,可以客戶端連進去,輸入 shutdown關閉服務端
或者 mysqladmin -uroot -p123 shutdown

2.4 初始化配置

2.4.0 作用

1
2
控制MySQL的啟動
影響到客戶端的連線

2.4.1 初始化配置的方法

1
2
3
預編譯
**配置檔案(所有啟動方式)**
命令列引數 (僅限於 mysqld_safe mysqld)

2.4.2 初始配置檔案

初始化配置檔案的預設讀取路徑

1
2
3
4
5
6
[root@db01 ~]# mysqld --help --verbose |grep my.cnf
/etc/my.cnf /etc/mysql/my.cnf /usr/local/mysql/etc/my.cnf ~/.my.cnf
注:
預設情況下,MySQL啟動時,會依次讀取以上配置檔案,如果有重複選項,會以最後一個檔案設定的為準。
# 手動指定使用哪個配置檔案啟動,指定預設檔案位置點
但是,如果啟動時加入了--defaults-file=xxxx時,以上的所有檔案都不會讀取.

配置檔案的書寫方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[標籤]
配置項=xxxx

標籤型別:
服務端:影響資料庫服務端執行
客戶端:隻影響本地客戶端連線,不影響遠端
伺服器端標籤:
[mysqld]
[mysqld_safe]
[server] 代表所有客戶端

客戶端標籤:
[mysql]
[mysqldump]
[client] 代表所有客戶端

配置檔案的示例展示:
[root@db01 ~]# cat /etc/my.cnf
[mysqld]
user=mysql # 負責資料庫管理的使用者
basedir=/app/mysql #軟體位置
datadir=/data/mysql #資料位置
socket=/tmp/mysql.sock #套接在檔案
server_id=6 #標識節點的編號,主從複製會用
port=3306 #當前節點埠號
log_error=/data/mysql/mysql.log # 日誌檔案位置
[mysql] # 客戶端標籤
socket=/tmp/mysql.sock

2.5 多例項的應用

2.5.1 準備多個目錄

1
2
mkdir -p /data/330{7,8,9}/data
mkdir -p /binlog/330{7,8,9}

2.5.2 準備配置檔案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
cat > /data/3307/my.cnf <<EOF
[mysqld]
basedir=/app/database/mysql
datadir=/data/3307/data
socket=/tmp/mysql3307.sock
log_error=/data/3307/mysql.log
port=3307
server_id=7
log_bin=/data/3307/mysql-bin
EOF

cat > /data/3308/my.cnf <<EOF
[mysqld]
basedir=/app/database/mysql
datadir=/data/3308/data
socket=/tmp/mysql3308.sock
log_error=/data/3308/mysql.log
port=3308
server_id=8
log_bin=/data/3308/mysql-bin
EOF

cat > /data/3309/my.cnf <<EOF
[mysqld]
basedir=/app/database/mysql
datadir=/data/3309/data
socket=/tmp/mysql3309.sock
log_error=/data/3309/mysql.log
port=3309
server_id=9
log_bin=/data/3309/mysql-bin
EOF

2.5.3 初始化三套資料

1
2
3
4
5
chown -R mysql.mysql /data /binlog
mv /etc/my.cnf /etc/my.cnf.bak
mysqld --initialize-insecure --user=mysql --datadir=/data/3307/data --basedir=/app/database/mysql
mysqld --initialize-insecure --user=mysql --datadir=/data/3308/data --basedir=/app/database/mysql
mysqld --initialize-insecure --user=mysql --datadir=/data/3309/data --basedir=/app/database/mysql

2.5.4 systemd管理多例項

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
cd /etc/systemd/system
cp mysqld.service mysqld3307.service
cp mysqld.service mysqld3308.service
cp mysqld.service mysqld3309.service


# 指令碼介紹
[Unit]
Description=MySQL Server # 描述服務
Documentation=man:mysqld(8)
Documentation=http://dev.mysql.com/doc/refman/en/using-systemd.html
After=network.target # 描述服務類別
After=syslog.target
[Install]
WantedBy=multi-user.target
[Service] # 服務執行引數設定
User=mysql
Group=mysql
ExecStart=/app/mysql/bin/mysqld --defaults-file=/data/3307/my.cnf
LimitNOFILE = 5000
EOF

cat >/etc/systemd/system/mysqld3307.service <<EOF
[Unit]
Description=MySQL Server
Documentation=man:mysqld(8)
Documentation=http://dev.mysql.com/doc/refman/en/using-systemd.html
After=network.target
After=syslog.target
[Install]
WantedBy=multi-user.target
[Service]
User=mysql
Group=mysql
ExecStart=/app/database/mysql/bin/mysqld --defaults-file=/data/3307/my.cnf
LimitNOFILE = 5000
EOF

cat >/etc/systemd/system/mysqld3308.service <<EOF
[Unit]
Description=MySQL Server
Documentation=man:mysqld(8)
Documentation=http://dev.mysql.com/doc/refman/en/using-systemd.html
After=network.target
After=syslog.target
[Install]
WantedBy=multi-user.target
[Service]
User=mysql
Group=mysql
ExecStart=/app/database/mysql/bin/mysqld --defaults-file=/data/3308/my.cnf
LimitNOFILE = 5000
EOF

cat >/etc/systemd/system/mysqld3309.service <<EOF
[Unit]
Description=MySQL Server
Documentation=man:mysqld(8)
Documentation=http://dev.mysql.com/doc/refman/en/using-systemd.html
After=network.target
After=syslog.target
[Install]
WantedBy=multi-user.target
[Service]
User=mysql
Group=mysql
ExecStart=/app/database/mysql/bin/mysqld --defaults-file=/data/3309/my.cnf
LimitNOFILE = 5000
EOF

2.5.5 授權

1
chown -R mysql.mysql /data/*

2.5.6 啟動

1
2
3
systemctl start mysqld3307.service
systemctl start mysqld3308.service
systemctl start mysqld3309.service

2.5.7 驗證多例項

1
2
3
4
netstat -lnp|grep 330
mysql -S /data/3307/mysql.sock -e "select @@server_id"
mysql -S /data/3308/mysql.sock -e "select @@server_id"
mysql -S /data/3309/mysql.sock -e "select @@server_id"

 

SQL基礎應用

一 SQL介紹

1
2
3
4
5
6
7
8
9
10
11
12
13
14
結構化查詢語言
有一些標準:89 92 99 03
5.7 以後符合SQL92嚴格模式
通過sql_mode引數來控制

# 檢視sql_mode,sql_mode用來規範sql語句的書寫方式
select @@sql_mode; # 檢視sql_mode
ONLY_FULL_GROUP_BY, # 5.7新加入
STRICT_TRANS_TABLES,
NO_ZERO_IN_DATE,
NO_ZERO_DATE,
ERROR_FOR_DIVISION_BY_ZERO,
NO_AUTO_CREATE_USER,
NO_ENGINE_SUBSTITUTION

二 常用SQL分類

1
2
3
4
5
6
7
8
9
# help:客戶端功能幫助
# help contents:服務端功能幫助
# help Data Definition
# help DROP DATABASE

DDL:資料定義語言
DCL:資料控制語言
DML:資料操作語言
DQL:資料的查詢語言

2.1 客戶端命令

image-20200813150640462

1
2
3
4
5
6
7
8
9
10
11
12
13
# 客戶端輸入help

# \c 結束上一條命令
# \G 格式化輸出
# exit,\q,control+d,quit退出會話
# notee和tee 開啟日誌記錄,
tee/tmp/mysql.log # 開啟,以後執行的sql都會被記錄到日誌,包括結果
select * from t2;
# source 匯入sql指令碼,類似於<,恢復備份
source /root/my.sql
# system 在mysql中執行linux命令
system ls
system cd /tmp && ls

三 資料型別、表屬性、字符集

3.1 資料型別

3.1.1 作用

1
保證資料的準確性和標準性。

3.1.2 種類

數值型別

image-20200813115647750

1
2
3
4
5
6
7
tinyint  : -128~127     1個位元組,8個位元位,正負2的7次方減1
int :-2^31~2^31-1 4個位元組,32個位元位,正負2的31次方減1
bigint : -2^63~2^63-1 8個位元組,64個位元位,正負2的63次方減1
說明:手機號是無法儲存到int的。一般是使用char型別來儲存收集號

# 建立表指定資料型別
create tabel t1(id int,name varchar(64),age tinyint)

字元型別

image-20200813115855936

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
char(11) :
定長 的字串型別,在儲存字串時,最大字元長度11個,立即分配11個字元長度的儲存空間,如果存不滿,空格填充。
varchar(11):
變長 的字串型別看,最大字元長度11個。在儲存字串時,自動判斷字元長度,按需分配儲存空間。
varchar型別,除了會儲存字串之外,還會額外使用1-2個位元組儲存字元長度
enum('bj','tj','sh'):
列舉型別,存字串型別,比較適合於將來此列的值是固定範圍內的特點,可以使用enum,可以很大程度的優化我們的索引結構。


# 括號中數字指的是字元長度,所以存英文和中文,其實佔得空間是不一樣的
# 英文和數字,一個字元是1個位元組,中文3個位元組,emoji佔4個位元組
# 因為編碼方式規定了utf8,所以不需要自行考慮此問題

# 測試:
create database db1 charset utf8mb4;
create table t2(id int,name varchar(10),sex char(10));
insert into t2 values(1,'aaaaaaaaaa','一二三四五六七八九十');
insert into t2 values(1,'aaaaaaaaaa','一二三四五六七八九十一');
insert into t2 values(1,'aaaaaaaaaa','1234567891');
ERROR 1406 (22001): Data too long for column 'sex' at row 1
desc t2;# 查看錶結構
select length(sex) from t2; # 檢視字元所佔用的空間

# mysql 5.6 超長會存進去,自動截斷
# mysql 5.6 超長會報錯

varchar最多能儲存65535個位元組的資料,一般我們最多定義varchar(255),超過255會被轉成text型別

時間型別

image-20200813115913262

1
2
3
4
5
DATETIME (8個位元組長度)
範圍為從 1000-01-01 00:00:00.000000 至 9999-12-31 23:59:59.999999。
TIMESTAMP (4個位元組長度)
1970-01-01 00:00:00.000000 至 2038-01-19 03:14:07.999999。
timestamp會受到時區的影響

二進位制型別

image-20200813115928772

json格式

5.6以後支援

3.2 表屬性

3.2.1 列屬性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
約束(一般建表時新增):
# primary key :主鍵約束,
唯一且非空,每個表只能有一個主鍵,作為聚簇索引
設定為主鍵的列,此列的值必須非空且唯一,主鍵在一個表中只能有一個,但是可以有多個列一起構成。

#not null :非空約束
列值不能為空,也是表設計的規範,儘可能將所有的列設定為非空。可以設定預設值為0

# unique key:唯一鍵
列值不能重複

# unsigned:無符號
針對數字列,非負數。

其他屬性:
# key:索引
可以在某列上建立索引,來優化查詢,一般是根據需要後新增
# default :預設值
列中,沒有錄入值時,會自動使用default的值填充
# auto_increment :自增長
針對數字列,順序的自動填充資料(預設是從1開始,將來可以設定起始點和偏移量)
# comment : 註釋

3.2.2 表的屬性

1
2
3
4
5
儲存引擎:
InnoDB(預設的)
字符集和排序規則:
utf8
utf8mb4

3.3 字符集和校對規則

3.3.1 字符集(charset)

1
2
3
4
5
6
7
8
9
10
# show charset;
有非常多,現在只關注如下兩種
utf8
utf8mb4 # 5.6以後出現,8.0以後預設使用utf8mb4,8.0以前預設是latin1(拉丁)
# 差別:
utf8:最大儲存長度,單個字元最多3個位元組
utf8mb4支援的編碼比utf8更多,比如emoji字元,emoji字元,一個字元佔4個位元組

# 檢視庫的字元編碼
show create databaes mysql

3.3.2 校對規則(排序規則,collation)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
show collation;
# 影響排序規則
a
b
A
aB
Ba
select ascii('a'); # 檢視a的ascii碼
如果大小寫敏感排序一個樣
如果大小寫敏感排序另一個樣

# 簡單來說就是:大小寫是否敏感,預設不敏感
+--------------------------+----------+-----+---------+----------+---------+
| Collation | Charset | Id | Default | Compiled | Sortlen |
+--------------------------+----------+-----+---------+----------+---------+
| utf8mb4_general_ci | utf8mb4 | 45 | Yes | Yes | 1 |
| utf8mb4_bin | utf8mb4 | 46 | | Yes | 1 |

四 DDL應用

4.1 資料定義語言

資料定義語言,對庫和表進行操作,操作mysql的物件,即庫和表,對元資料進行操作

4.2 庫定義

4.2.1 建立

4.2.1 建立資料庫

1
2
3
4
5
6
7
8
9
10
11
12
create database school;
create schema sch;
show charset; # 檢視支援的字符集
show collation; # 檢視支援的校對規則,collation
CREATE DATABASE test CHARSET utf8;
create database xyz charset utf8mb4 collate utf8mb4_bin;

建庫規範:
1.庫名不能有大寫字母
2.建庫要加字符集
3.庫名不能有數字開頭,不能使用保留欄位(database,table)
4.庫名要和業務相關

建庫標準語句

1
2
mysql> create database db charset utf8mb4;
mysql> show create database xuexiao;

4.2.2 刪除(生產中禁止使用)

1
mysql> drop database lqz;

4.2.3 修改

1
2
3
4
5
6
SHOW CREATE DATABASE school;
ALTER DATABASE school CHARSET utf8;
# 注意:
只能改庫屬性,不能改庫名(只能改字符集)
修改字符集,修改後的字符集一定是原字符集的嚴格超集
從小往大改,從utf8改到utf8mb4可以,從utf8mb4改成utf8可能會亂碼

4.2.4 查詢庫相關資訊(DQL)

1
2
show databases; # 檢視庫
show create database lqz; # 檢視具體資訊

4.3 表定義

4.3.1 建立

1
2
3
4
5
create table stu(
列1 屬性(資料型別、約束、其他屬性) ,
列2 屬性,
列3 屬性
)

4.3.2 建表

1
2
3
4
5
6
7
8
9
USE school;
CREATE TABLE stu(
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '學號',
sname VARCHAR(255) NOT NULL COMMENT '姓名',
sage TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '年齡',
sgender ENUM('m','f','n') NOT NULL DEFAULT 'n' COMMENT '性別' ,
sfz CHAR(18) NOT NULL UNIQUE COMMENT '身份證',
intime TIMESTAMP NOT NULL DEFAULT NOW() COMMENT '入學時間'
) ENGINE=INNODB CHARSET=utf8 COMMENT '學生表';

建表規範:

1
2
3
4
5
6
7
1. 表名小寫(多平臺相容性問題,window不區分大小寫,linux嚴格大小寫),
2. 不能是數字開頭,名字不要太長15個字元以內
3. 注意字符集和儲存引擎
4. 表名和業務有關,不能使用關鍵字
5. 選擇合適的資料型別:合適,簡短,足夠
6. 必須有主鍵,每個列都要有註釋
7. 每個列設定為非空,無法保證非空,用0來填充。

4.3.2 刪除(生產中禁用命令)

1
drop table t1;

4.3.3 修改

  1. 在stu表中新增qq列
1
2
DESC stu;
ALTER TABLE stu ADD qq VARCHAR(20) NOT NULL UNIQUE COMMENT 'qq號';
  1. 在sname後加微信列
1
ALTER TABLE stu ADD wechat VARCHAR(64) NOT NULL UNIQUE  COMMENT '微訊號' AFTER sname ;
  1. 在id列前加一個新列num
1
2
ALTER TABLE stu ADD num INT NOT NULL COMMENT '數字' FIRST;
DESC stu;
  1. 把剛才新增的列都刪掉(危險)
1
2
3
ALTER TABLE stu DROP num;
ALTER TABLE stu DROP qq;
ALTER TABLE stu DROP wechat;
  1. 修改sname資料型別的屬性
1
ALTER TABLE stu MODIFY sname VARCHAR(128)  NOT NULL ;
  1. 將sgender 改為 sg 資料型別改為 CHAR 型別(change需要把原來不需要修改的也帶上)
1
2
ALTER TABLE stu CHANGE sgender sg CHAR(1) NOT NULL DEFAULT 'n' ;
DESC stu;

注意:

在mysql中,DDL語句在對錶進行操作時,是要鎖“元資料表”的,此時,所有修改類的命令無法執行

(元資料:記錄表的各種資訊,對資料鎖定,才能修改,否則都去改,就會出問題)

大表加一列,業務繁忙的表,要謹慎

8.0以前版本需要藉助,可以藉助pt-osc(pt-online-shaema-change),gh-ost工具進行DDL操作

4.3.4 表屬性查詢(DQL)

1
2
3
4
5
use school
show tables; # 檢視該庫下所有表
desc stu; # 查看錶結構
show create table stu;# 檢視詳細建表語句
CREATE TABLE ceshi LIKE stu;

五 DCL應用 ****

1
2
grant 
revoke

六 DML應用

6.1 作用

1
對錶中的資料行進行增、刪、改

6.2 insert

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
--- 最標準的insert語句
INSERT INTO stu(id,sname,sage,sg,sfz,intime)
VALUES
(1,'zs',18,'m','123456',NOW());
SELECT * FROM stu;
--- 省事的寫法
INSERT INTO stu
VALUES
(2,'ls',18,'m','1234567',NOW());
--- 針對性的錄入資料
INSERT INTO stu(sname,sfz)
VALUES ('w5','34445788');
--- 同時錄入多行資料
INSERT INTO stu(sname,sfz)
VALUES
('w55','3444578d8'),
('m6','1212313'),
('aa','123213123123');
SELECT * FROM stu;


# HWM:記錄自增數字,高水位線

6.3 update

1
2
3
4
DESC stu;
SELECT * FROM stu;
UPDATE stu SET sname='zhao4' WHERE id=2;
注意:update語句必須要加where。

6.4 delete(危險!!)

1
DELETE FROM stu  WHERE id=3;

全表刪除:

1
2
3
4
5
6
7
8
9
10
11
12
DELETE FROM stu;
truncate table stu;
drop table stu;
區別:
delete: DML操作, 是邏輯性質刪除,逐行進行刪除,資料庫很多,速度慢,並沒有在磁碟上真正刪除,磁碟空間不會立即釋放,自增的值,也不會釋放(HWM高水位線不會降低),
truncate: DDL操作,物理層次刪除,對與表段中的資料頁進行清空,速度快,立即釋放控制元件,HWM高水位線會降低
drop:將表結構(元資料)和物理層次刪除
# 常規方法:
以上三者,都能通過備份+日誌,恢復資料
# 靈活辦法:
delete 可以通過翻轉日誌(binlog)
三種刪除資料的情況,可以通過《延時從庫》進行恢復

偽刪除:

用update來替代delete,最終保證業務中查不到(select)即可

1
2
3
4
5
6
7
1.新增狀態列
ALTER TABLE stu ADD state TINYINT NOT NULL DEFAULT 1 ;
SELECT * FROM stu;
2. UPDATE 替代 DELETE
UPDATE stu SET state=0 WHERE id=6;
3. 業務語句查詢
SELECT * FROM stu WHERE state=1;

七 DQL應用(select )

7.1 單獨使用

1
2
3
4
5
6
7
8
9
-- select @@xxx 檢視系統引數,配置檔案中配的都可以檢視
SELECT @@port;
SELECT @@basedir;
SELECT @@datadir;
SELECT @@socket;
SELECT @@server_id;

show variables; # mysql中的500多個引數
show variables like '%or%';

– select 函式();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
SELECT NOW(); # 當前時間
SELECT DATABASE(); # 當前資料庫
SELECT USER(); # 當前登入使用者
SELECT CONCAT("hello world"); # 字串拼接
SELECT CONCAT(USER,"@",HOST) FROM mysql.user;
SELECT GROUP_CONCAT(USER,"@",HOST) FROM mysql.user;
select version(); # 當前資料庫版本號
# 相關函式查詢,官方文件或者使用help
https://dev.mysql.com/doc/refman/5.7/en/func-op-summary-ref.html?tdsourcetag=s_pcqq_aiomsg

help contents
help functions
help String Functions

# 標準sql,其他關係型資料庫,必須要有from
select NOW() from dual;

# 計算
select 10*100

預設執行順序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
select 列,列
from 表1,表2。。
where 過濾條件1 過濾條件2。。
group by 條件1 條件2。。
having 過濾條件1 過濾條件2。。
order by 條件列1 條件列2
limit 限制

# d
select
1 from 表1,表2。。。
2 where 過濾條件1 過濾條件2。。
3 group by 條件1 條件2。。
3.5 select_list name,age 列名列表
4 having 過濾條件1 過濾條件2。。
5 order by 條件列1 條件列2
6 limit 限制


# 完整select 執行順序
(6) SELECT 列
(8) DISTINCT <select_list>
(1) FROM <left_table>
(3) <join_type> JOIN <right_table>
(2) ON <join_condition>
(4) WHERE <where_condition>
(5) GROUP BY <group_by_list>
(7) HAVING <having_condition>
(9) ORDER BY <order_by_condition>
(10) LIMIT <limit_number>

匯入資料

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql < world.sql
庫:world
表:
city
country
countrylanguage


city:城市表
DESC city;
ID : 城市ID
NAME : 城市名
CountryCode: 國家程式碼,比如中國CHN 美國USA
District : 區域
Population : 人口

7.2 單表子句-from

1
2
SELECT 列1,列2 FROM 表
SELECT * FROM 表

例子:
– 查詢city中所有的資料(不要對大表進行操作)

1
SELECT * FROM city ;

– 查詢city表中,id和姓名

1
SELECT id ,name  FROM city;

7.3 單表子句-where

1
SELECT col1,col2 FROM TABLE WHERE colN 條件;

7.3.1 where配合等值查詢

例子:
– 查詢中國(CHN)所有城市資訊

1
SELECT * FROM city WHERE countrycode='CHN';

– 查詢北京市的資訊

1
SELECT * FROM city WHERE NAME='peking';

– 查詢甘肅省所有城市資訊

1
SELECT * FROM city WHERE district='gansu';

7.3.2 where配合比較操作符(> < >= <= <>)

例子:
– 查詢世界上少於100人的城市

1
SELECT * FROM city WHERE population<100;

7.3.3 where配合邏輯運算子(and or )

例子:
– 中國人口數量大於500w

1
SELECT * FROM city WHERE countrycode='CHN' AND population>5000000;

– 中國或美國城市資訊

1
SELECT * FROM city WHERE countrycode='CHN' OR countrycode='USA';

7.3.4 where配合模糊查詢

例子:
– 查詢省的名字前面帶guang開頭的

1
2
SELECT * FROM city WHERE district LIKE 'guang%';    
注意:%不能放在前面,因為不走索引.只能用字串的列

7.3.5 where配合in語句

– 中國或美國城市資訊

1
2
3
4
5
6
7
SELECT * FROM city WHERE countrycode ='CHN'  or countrycode ='USA';
SELECT * FROM city WHERE countrycode IN ('CHN' ,'USA');
# 錯誤
SELECT * FROM city WHERE countrycode ='CHN' or countrycode ='USA'AND population>5000000;

# 正確
SELECT * FROM city WHERE countrycode IN ('CHN' ,'USA') AND population>5000000;

7.3.6 where配合between and

例子:
– 查詢世界上人口數量大於100w小於200w的城市資訊

1
2
SELECT * FROM city  WHERE population >1000000 AND population <2000000;
SELECT * FROM city WHERE population BETWEEN 1000000 AND 2000000;

7.4 group by + 常用聚合函式

7.4.1 作用

1
根據 by後面的條件進行分組,方便統計,by後面跟一個列或多個列

image-20200813182847212

7.4.2 常用聚合函式

1
2
3
4
5
6
**max()**      :最大值
**min()** :最小值
**avg()** :平均值
**sum()** :總和
**count()** :個數
group_concat() : 列轉行

7.4.3 例子:

例子1:統計世界上每個國家的總人口數.

1
2
USE world
SELECT countrycode ,SUM(population) FROM city GROUP BY countrycode;

例子2: 統計中國各個省的總人口數量(練習)

1
SELECT district,SUM(Population) FROM city  WHERE countrycode='chn' GROUP BY district;

例子3:統計世界上每個國家的城市數量(練習)

1
SELECT countrycode,COUNT(id)  FROM city GROUP BY countrycode;

例子4:統計中國,每個省總人口,城市個數,城市名列表(重點)

1
2
3
4
5
select district,sum(Population),count(id),name from world.city where countrycode='CHN' group by district;
# 報錯:
ERROR 1055 (42000): Expression #4 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'world.city.Name' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
# 原因:違反了sql_mode=only_full_group_by,SELECT list必須是group by的欄位和聚合函式,name不屬於,因為name有很多,在mysql5.6之前可以查詢,但是隻取name的第一個,5.7以後直接報錯,這是合理的
select district,sum(Population),count(id),group_concat(name) from world.city where countrycode='CHN' group by district;

7.5 having

1
where|group|having

例子4:統計中國每個省的總人口數,只打印總人口數小於100w

1
2
3
4
5
SELECT district,SUM(Population)
FROM city
WHERE countrycode='chn'
GROUP BY district
HAVING SUM(Population) < 1000000 ;

7.6 order by + limit

7.6.1 作用

1
實現先排序,by後新增條件列

7.6.2 應用案例

  1. 檢視中國所有的城市,並按人口數進行排序(從大到小)
1
SELECT * FROM city WHERE countrycode='CHN' ORDER BY population DESC;
  1. 統計中國各個省的總人口數量,按照總人口從大到小排序
1
2
3
4
5
SELECT district AS 省 ,SUM(Population) AS 總人口
FROM city
WHERE countrycode='chn'
GROUP BY district
ORDER BY 總人口 DESC ;
  1. 統計中國,每個省的總人口,找出總人口大於500w的,並按總人口從大到小排序,只顯示前三名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
SELECT  district, SUM(population)  FROM  city 
WHERE countrycode='CHN'
GROUP BY district
HAVING SUM(population)>5000000
ORDER BY SUM(population) DESC
LIMIT 3 ;

LIMIT N ,M --->跳過N,顯示一共M行
LIMIT 5,5
LIMIT 5 OFFSET 5;

SELECT district, SUM(population) FROM city
WHERE countrycode='CHN'
GROUP BY district
HAVING SUM(population)>5000000
ORDER BY SUM(population) DESC
LIMIT 5,5;

7.7 distinct:去重複

1
2
SELECT countrycode FROM city ;
SELECT DISTINCT(countrycode) FROM city ;

7.8 聯合查詢- union all

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 求並集
-- 中國或美國城市資訊
SELECT * FROM city
WHERE countrycode IN ('CHN' ,'USA');

SELECT * FROM city
WHERE countrycode ='CHN' or countrycode ='USA';

SELECT * FROM city WHERE countrycode='CHN'
UNION ALL
SELECT * FROM city WHERE countrycode='USA'

說明:一般情況下,我們會將 IN 或者 OR 語句 改寫成 UNION ALL,來提高效能
都是聚合兩個結果集
UNION 去重複
UNION ALL 不去重複

7.9 join 多表連線查詢

7.9.0 案例準備

按需求建立一下表結構:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use school
student :學生表
sno: 學號
sname:學生姓名
sage: 學生年齡
ssex: 學生性別

teacher :教師表
tno: 教師編號
tname:教師名字

course :課程表
cno: 課程編號
cname:課程名字
tno: 教師編號

score :成績表
sno: 學號
cno: 課程編號
score:成績

-- 專案構建
drop database school;
CREATE DATABASE school CHARSET utf8;
USE school

CREATE TABLE student(
sno INT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '學號',
sname VARCHAR(20) NOT NULL COMMENT '姓名',
sage TINYINT UNSIGNED NOT NULL COMMENT '年齡',
ssex ENUM('f','m') NOT NULL DEFAULT 'm' COMMENT '性別'
)ENGINE=INNODB CHARSET=utf8;

CREATE TABLE course(
cno INT NOT NULL PRIMARY KEY COMMENT '課程編號',
cname VARCHAR(20) NOT NULL COMMENT '課程名字',
tno INT NOT NULL COMMENT '教師編號'
)ENGINE=INNODB CHARSET utf8;

CREATE TABLE sc (
sno INT NOT NULL COMMENT '學號',
cno INT NOT NULL COMMENT '課程編號',
score INT NOT NULL DEFAULT 0 COMMENT '成績'
)ENGINE=INNODB CHARSET=utf8;

CREATE TABLE teacher(
tno INT NOT NULL PRIMARY KEY COMMENT '教師編號',
tname VARCHAR(20) NOT NULL COMMENT '教師名字'
)ENGINE=INNODB CHARSET utf8;

INSERT INTO student(sno,sname,sage,ssex)
VALUES (1,'zhang3',18,'m');

INSERT INTO student(sno,sname,sage,ssex)
VALUES
(2,'zhang4',18,'m'),
(3,'li4',18,'m'),
(4,'wang5',19,'f');

INSERT INTO student
VALUES
(5,'zh4',18,'m'),
(6,'zhao4',18,'m'),
(7,'ma6',19,'f');

INSERT INTO student(sname,sage,ssex)
VALUES
('oldboy',20,'m'),
('oldgirl',20,'f'),
('oldp',25,'m');


INSERT INTO teacher(tno,tname) VALUES
(101,'oldboy'),
(102,'hesw'),
(103,'oldguo');

DESC course;
INSERT INTO course(cno,cname,tno)
VALUES
(1001,'linux',101),
(1002,'python',102),
(1003,'mysql',103);

DESC sc;
INSERT INTO sc(sno,cno,score)
VALUES
(1,1001,80),
(1,1002,59),
(2,1002,90),
(2,1003,100),
(3,1001,99),
(3,1003,40),
(4,1001,79),
(4,1002,61),
(4,1003,99),
(5,1003,40),
(6,1001,89),
(6,1003,77),
(7,1001,67),
(7,1003,82),
(8,1001,70),
(9,1003,80),
(10,1003,96);

SELECT * FROM student;
SELECT * FROM teacher;
SELECT * FROM course;
SELECT * FROM sc;

7.9.1 語法

1
2
3
4
5
6
7
8
9
10
笛卡爾積:select * from teacher,course;
內連線(inner join):
select * from teacher join course on teacher.tno=course.tno;
select city.name,country.name,city.population from city join country on city.countrycode = country.code and city.population <100;
外連線:
left join:左表所有資料和右表滿足條件的資料
select * from teacher join course on teacher.tno=course.tno;
select city.name,country.name,city.population from city left join country on city.countrycode = country.code and city.population <100;
right join:右表所有資料和左表滿足條件的資料
select city.name,country.name,city.population from city right join country on city.countrycode = country.code and city.population <100;

image-20200813203607215

查詢張三的家庭住址

1
2
3
4
SELECT A.name,B.address FROM
A JOIN B
ON A.id=B.id
WHERE A.name='zhangsan'

7.9.2 例子:

  1. 查詢一下世界上人口數量小於100人的城市名和國家名
1
2
3
4
5
SELECT b.name ,a.name ,a.population
FROM city AS a
JOIN country AS b
ON b.code=a.countrycode
WHERE a.Population<100
  1. 查詢城市shenyang,城市人口,所在國家名(name)及國土面積(SurfaceArea)
1
2
3
4
SELECT a.name,a.population,b.name ,b.SurfaceArea
FROM city AS a JOIN country AS b
ON a.countrycode=b.code
WHERE a.name='shenyang';

7.9.3 別名

1
2
3
4
5
6
7
8
9
列別名,表別名
SELECT
a.Name AS an ,
b.name AS bn ,
b.SurfaceArea AS bs,
a.Population AS bp
FROM city AS a JOIN country AS b
ON a.CountryCode=b.Code
WHERE a.name ='shenyang';

7.9.4 多表SQL練習題

  1. 統計zhang3,學習了幾門課
1
2
3
4
5
6
SELECT st.sname , COUNT(sc.cno)
FROM student AS st
JOIN
sc
ON st.sno=sc.sno
WHERE st.sname='zhang3'
  1. 查詢zhang3,學習的課程名稱有哪些?
1
2
3
4
5
6
7
SELECT st.sname , GROUP_CONCAT(co.cname)
FROM student AS st
JOIN sc
ON st.sno=sc.sno
JOIN course AS co
ON sc.cno=co.cno
WHERE st.sname='zhang3'
  1. 查詢oldguo老師教的學生名.
1
2
3
4
5
6
7
8
9
SELECT te.tname ,GROUP_CONCAT(st.sname)
FROM student AS st
JOIN sc
ON st.sno=sc.sno
JOIN course AS co
ON sc.cno=co.cno
JOIN teacher AS te
ON co.tno=te.tno
WHERE te.tname='oldguo';
  1. 查詢oldguo所教課程的平均分數
1
2
3
4
5
6
7
SELECT te.tname,AVG(sc.score)
FROM teacher AS te
JOIN course AS co
ON te.tno=co.tno
JOIN sc
ON co.cno=sc.cno
WHERE te.tname='oldguo'

4.1 每位老師所教課程的平均分,並按平均分排序

1
2
3
4
5
6
7
8
SELECT te.tname,AVG(sc.score)
FROM teacher AS te
JOIN course AS co
ON te.tno=co.tno
JOIN sc
ON co.cno=sc.cno
GROUP BY te.tname
ORDER BY AVG(sc.score) DESC ;
  1. 查詢oldguo所教的不及格的學生姓名
1
2
3
4
5
6
7
8
9
SELECT te.tname,st.sname,sc.score
FROM teacher AS te
JOIN course AS co
ON te.tno=co.tno
JOIN sc
ON co.cno=sc.cno
JOIN student AS st
ON sc.sno=st.sno
WHERE te.tname='oldguo' AND sc.score<60;

5.1 查詢所有老師所教學生不及格的資訊

1
2
3
4
5
6
7
8
9
SELECT te.tname,st.sname,sc.score
FROM teacher AS te
JOIN course AS co
ON te.tno=co.tno
JOIN sc
ON co.cno=sc.cno
JOIN student AS st
ON sc.sno=st.sno
WHERE sc.score<60;

注意:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
join 左邊的為左表,右邊的為右邊

驅動表:驅動表會拿著每一行,逐一的跟另一個表匹配,匹配成功就合併
#雙層for迴圈
for 每一行 in 驅動表:
for 每一行 in 另一個表:
匹配成功合併
# 假設驅動表是1000行,另一個表是10行,至少要1000次迴圈,如果拿另一個表去找,最少10次
多表關聯,驅動表要選小表,降低next loop次數

對於內連線來講,我們沒法控制驅動表是誰,完全由優化器決定,如果要人為干預,要把內連線寫成外連線的方式
如果使用left join 可以強制左表為驅動表

總結:
1 小表作為驅動表,降低next loop次數
2 left join 可以強制左表為驅動表

7.9.5 綜合練習

1
2
3
4
5
6
7
8
9
1. 查詢平均成績大於60分的同學的學號和平均成績;
2. 查詢所有同學的學號、姓名、選課數、總成績;
3. 查詢各科成績最高和最低的分:以如下形式顯示:課程ID,最高分,最低分
4. 統計各位老師,所教課程的及格率
5. 查詢每門課程被選修的學生數
6. 查詢出只選修了一門課程的全部學生的學號和姓名
7. 查詢選修課程門數超過1門的學生資訊
8. 統計每門課程:優秀(85分以上),良好(70-85),一般(60-70),不及格(小於60)的學生列表
9. 查詢平均成績大於85的所有學生的學號、姓名和平均成績

7.9.6 補充

別名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1 表別名
SELECT te.tname,st.sname,sc.score
FROM teacher AS te
JOIN course AS co
ON te.tno=co.tno
JOIN sc
ON co.cno=sc.cno
JOIN student AS st
ON sc.sno=st.sno
WHERE sc.score<60;

# 表別名可以在全域性使用,只在當次查詢中有用

# 2 列別名
select student.sno as '學號',student.sname as '姓名' from studnet;
# 列別名在哪些子句中可以使用
在having之前都不能使用(參照上面select執行順序)

 

八 元資料資訊

8.1 邏輯表有關組成部分

image-20200814010107629

1
2
3
4
5
6
# 每次資料庫啟動,會自動在記憶體中生成nformation_schema,生成查詢mysql部分元資料的檢視
# 檢視:select 語句的執行方法,不儲存資料本身
## 建立檢視
create view v_city as select name from city where id <10;
## 使用檢視
select * from v_city;

8.2 information_schema.tables檢視

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
use information_schema; # 進入information_schema
show tables; # 可以看到所有檢視
# 我們研究tables這個檢視
desc tables;
# DESC information_schema.TABLES
TABLE_SCHEMA ---->表所在庫名
TABLE_NAME ---->表名
ENGINE ---->引擎
TABLE_ROWS ---->表的行數(不是特別實時)
AVG_ROW_LENGTH ---->表中行的平均行長度(位元組)
INDEX_LENGTH ---->索引的佔用空間大小(位元組)
DATA_FREE ---->表中是否有碎片

# 所有的
TABLE_CATALOG
TABLE_SCHEMA
TABLE_NAME
TABLE_TYPE
ENGINE
VERSION
ROW_FORMAT
TABLE_ROWS
AVG_ROW_LENGTH
DATA_LENGTH
MAX_DATA_LENGTH
INDEX_LENGTH
DATA_FREE
AUTO_INCREMENT
CREATE_TIME
UPDATE_TIME
CHECK_TIME
TABLE_COLLATION
CHECKSUM
CREATE_OPTIONS
TABLE_COMMENT

# 使用場景
資產統計,自動化運維平臺,多少個庫,多少個表,佔用空間怎麼樣,統計表增長
  1. 查詢整個資料庫中所有庫和所對應的表資訊
1
2
3
4

SELECT table_schema,GROUP_CONCAT(table_name)
FROM information_schema.tables
GROUP BY table_schema;
  1. 統計所有庫下的表個數
1
2
3
SELECT table_schema,COUNT(table_name)
FROM information_schema.TABLES
GROUP BY table_schema
  1. 查詢所有innodb引擎的表及所在的庫
1
2
SELECT table_schema,table_name,ENGINE FROM information_schema.`TABLES`
WHERE ENGINE='innodb';
  1. 統計world資料庫下每張表的磁碟空間佔用
1
2
SELECT table_name,CONCAT((TABLE_ROWS*AVG_ROW_LENGTH+INDEX_LENGTH)/1024," KB")  AS size_KB
FROM information_schema.tables WHERE TABLE_SCHEMA='world';
  1. 統計所有資料庫的總的磁碟空間佔用
1
2
3
4
5
6
SELECT
TABLE_SCHEMA,
CONCAT(SUM(TABLE_ROWS*AVG_ROW_LENGTH+INDEX_LENGTH)/1024," KB") AS Total_KB
FROM information_schema.tables
GROUP BY table_schema;
mysql -uroot -p123 -e "SELECT TABLE_SCHEMA,CONCAT(SUM(TABLE_ROWS*AVG_ROW_LENGTH+INDEX_LENGTH)/1024,' KB') AS Total_KB FROM information_schema.tables GROUP BY table_schema;"
  1. 統計每個庫,所有表的個數,表名
1
select table_schema,count(table_name),GROUP_CONCAT(table_name) from TABLES group by table_schema;
  1. 統計每個庫佔用空間大小
1
2
3
select table_schema,sum(AVG_ROW_LENGTH*TABLE_ROWS+INDEX_LENGTH)/1024 from information_schema.tables group by table_schema;

select table_schema,sum(DATA_LENGTH)/1024 from information_schema.tables group by table_schema;
  1. 生成整個資料庫下的所有表的單獨備份語句
1
2
3
4
5
6
7
8
模板語句:
mysqldump -uroot -p123 world city >/tmp/world_city.sql
SELECT CONCAT("mysqldump -uroot -p123 ",table_schema," ",table_name," >/tmp/",table_schema,"_",table_name,".sql" )
FROM information_schema.tables
WHERE table_schema NOT IN('information_schema','performance_schema','sys')
INTO OUTFILE '/tmp/bak.sh' ;

CONCAT("mysqldump -uroot -p123 ",table_schema," ",table_name," >/tmp/",table_schema,"_",table_name,".sql" )
  1. 107張表,都需要執行以下2條語句
1
2
3
4
5
6
ALTER TABLE world.city DISCARD TABLESPACE;
ALTER TABLE world.city IMPORT TABLESPACE;
SELECT CONCAT("alter table ",table_schema,".",table_name," discard tablespace")
FROM information_schema.tables
WHERE table_schema='world'
INTO OUTFILE '/tmp/dis.sql';

九 show 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
show  databases;                        #檢視所有資料庫
show tables; #檢視當前庫的所有表
SHOW TABLES FROM #檢視某個指定庫下的表
show create database world #檢視建庫語句
show create table world.city #檢視建表語句
show grants for root@'localhost' #檢視使用者的許可權資訊
show charset; #檢視字符集
show collation #檢視校對規則
show processlist; #檢視資料庫連線情況
show full processlist; #檢視資料庫連線情況詳細資訊
show privileges #檢視許可權資訊
show index from #表的索引情況
show status #資料庫狀態檢視
SHOW STATUS LIKE '%lock%'; #模糊查詢資料庫某些狀態
SHOW VARIABLES #檢視所有配置資訊
SHOW variables LIKE '%lock%'; #檢視部分配置資訊
show engines #檢視支援的所有的儲存引擎
show engine innodb status\G #檢視InnoDB引擎相關的狀態資訊
show binary logs #列舉所有的二進位制日誌
show master status #檢視資料庫的日誌位置資訊
show binlog evnets in #檢視二進位制日誌事件
show slave status \G #檢視從庫狀態
SHOW RELAYLOG EVENTS in #檢視從庫relaylog事件資訊
desc (show colums from city) #查看錶的列定義資訊
http://dev.mysql.com/doc/refman/5.7/en/show.html
help show 檢視其它的

 

索引及執行計劃

一 索引作用

1
提供了類似於書中目錄的作用,目的是為了優化查詢

二 索引的種類(演算法)

1
2
3
4
5
B樹索引:b tree, B+tree,B*tree
Hash索引
R樹
Full text
GIS

三 B樹 基於不同的查詢演算法分類介紹

B 樹

image-20200815112027336

B+樹

image-20200815113128272

B*樹

在b+tree基礎上,枝節點也加入了雙向指標(Innodb,使用B*樹)

image-20200815112517707

1
2
3
B-tree
B+Tree 在範圍查詢方面提供了更好的效能(> < >= <= like)
B*Tree

四 在功能上的分類

4.1 聚簇索引構建B樹(簇就是區)

4.1.1 前提

1
2
3
4
(1)建表時,指定了主鍵列,MYSQL InnoDB會將主鍵作為聚簇索引列,比如 id not null primary key
(2)如果沒有主鍵,會選擇唯一鍵(unique)作為聚集索引.
(3)聚簇必須在建表時才有意義,一般是表的無關列(ID)
(4)如果以上都沒有,自動生成隱藏的聚簇索引

4.1.2 作用

有了聚簇索引,將來插入的資料行,在同一個區內,都會按照id值的順序,有序儲存資料

4.2.3 聚簇索引構建B樹過程

image-20200815143400014

1
2
3
4
5
6
7
8
9
10
11
12
13
段:一個表就是一個段,可以由一個或者多個區構成
區/簇:一個區(簇),預設1M,連續的64個頁(pages),一張表由多個簇構成
頁:一個頁,預設16k,連續的4個os的block,最小的儲存單元

# 注意
上圖只是舉例說明,並不是一個葉子節點只存4行資料
枝節點也是由一個頁儲存,當然儲存的資料可能更多
一顆b樹索引至少要有root節點和葉子節點,枝節點可以沒有(資料量少的情況)
聚簇索引的作用:拿主鍵列去查詢的時候,可以快速鎖定要查詢的資料行所在的頁,3次io
如果沒有這個,需要全表掃描,代價很高,只能加速有主鍵列的查詢速度,所以按主鍵查,是效率最高的
mysql 的 innoDB的表,是聚簇索引組織儲存資料表,每個頁是稀疏儲存,不一定全存滿整個頁

# 其他列怎麼辦?引出輔助索引

4.2 輔助索引(S)構建B+樹

4.2.1 前提

1
在除主鍵以外的普通列上構建索引,例如name欄位

4.2.2 作用

1
優化非聚簇索引列之外的查詢

4.2.3 輔助索引構建B樹過程

image-20200815150924566

1
2
3
4
5
6
7
8
9
10
# 查詢時,拿著name=er去一層一層找到er這個值,對的id,因為查的是*,所有,通過id再去原來的聚簇索引中找具體資料(回表過程)

(1). 索引是基於表中,列(索引鍵)的值生成的B樹結構
(2). 首先提取此列所有的值,進行自動排序
(3). 將排好序的值,均勻的分佈到索引樹的葉子節點中(16K)
(4). 然後生成此索引鍵值所對應得後端資料頁的指標
(5). 生成枝節點和根節點,根據資料量級和索引鍵長度,生成合適的索引樹高度
id name age gender
select * from t1 where id=10;
問題: 基於索引鍵做where查詢,對於id列是順序IO,但是對於其他列的查詢,可能是隨機IO.

4.4 聚簇索引和輔助索引構成區別

1
2
3
4
5
# 聚集索引只能有一個,非空唯一,一般時主鍵
# 輔助索引,可以有多個,時配合聚集索引使用的
# 聚簇索引葉子節點,就是磁碟的資料行儲存的資料頁,輔助索引不存整體資料
# MySQL是根據聚簇索引,組織儲存資料,資料儲存時就是按照聚集索引的順序進行儲存資料
# 輔助索引,只會提取索引鍵值,進行自動排序生成B樹結構

五 輔助索引細分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 1.普通的單列輔助索引
# 2.聯合索引(多列構建一個索引)
多個列作為索引條件,生成索引樹,理論上設計的好的,可以減少大量的回表
如果 select * from t1 where name='er' and gender='男'; 這種比較多,建議name和gender建聯合索引
構建索引過程相同,只不過現在按name和gender兩列排序,生成枝節點時,只儲存最左列(name)的值,不會存所有索引列(name和gender),所以,重複值少的列,放在最左側

聯合索引的:注意最左原則(a,b,c 建立索引,相當於a索引,ab索引,abc索引)
1 查詢條件中,必須包含最左列,上面的例子就是a列,只有b,c走不了索引,
2 建立索引時,一定要選擇重複值少的列,作為最左列
# 全覆蓋
select * from t1 where a = and b= or c= # 走索引(極小情況不走索引:索引失效,統計資訊不真實)
select * from t1 where a = and b in and c in # in條件等同於=,也會走索引
select * from t1 where c = and b= and a = # 也會走索引,因為sql優化器會把a位置調整
select * from t1 where a = and b= order by c #全覆蓋
# 部分覆蓋
select * from t1 where a =
select * from t1 where a = and b=
select * from t1 where a = and c=
select * from t1 where a = and b < > >= <= like and c= # 不等值,只能覆蓋到a,b 不能覆蓋到c
select * from t1 where a < > >= <= like and b = like and c= # 不等值,只能覆蓋到a
select * from t1 where a = order by b # 走ab的索引
select * from t1 where c = order by a # 就不走索引,多子句要按照執行順序建立聯合索引,c和a沒有按順序,不會走索引
# 不覆蓋
bc
b
c
# 3.唯一索引
索引列的值都是唯一的.
# 4.字首索引
假設建立索引的列非常長,我們選擇的索引列值長度過長(一個頁儲存的資料固定),會導致索引樹變高,導致io次數變多
mysql中建議索引樹高度3--4層,800w--1000w行,20--30個列,會在3--4層之間
資料量特別少,也會有兩層,根和葉子
只取大欄位的前幾個字元,作為索引生成條件

六 關於索引樹的高度受什麼影響

1
2
3
4
5
6
7
8

# 那些因素導致
1. 資料行過多,資料量級大, 解決方法:分割槽表(分庫分表),歸檔表(一個月生成一個表:手工,pt-archive),分散式架構
2. 索引列值過長 , 解決方法:字首索引
3. 資料型別:(選擇合適的資料型別)
變長長度字串,使用了char,解決方案:變長字串使用varchar
enum型別的使用enum ('山東','河北','黑龍江','吉林','遼寧','陝西'......),enum更加省空間
1 2 3

七 索引的基本管理

7.1 索引建立前

1
2
3
4
5
6
# 什麼情況下建索引
按業務語句的需求建立合適的索引,並不是將所有列都建立索引(不是越多越好)
將索引建立在經常where,group by order by join on 的條件
# 為什麼不能亂建索引
1 插入,刪除資料,都會涉及到索引樹的更新,如果冗餘索引過多,表的資料變化,可能會導致索引頻繁更新,會阻塞正常業務的更新請求
2 索引過多,會導致優化器選擇出現偏差,效能可能達不到預想的效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
db01 [world]>desc city; # 查看錶的索引情況
+-------------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+----------+------+-----+---------+----------------+
| ID | int(11) | NO | PRI | NULL | auto_increment |
| Name | char(35) | NO | | | |
| CountryCode | char(3) | NO | MUL | | |
| District | char(20) | NO | | | |
| Population | int(11) | NO | | 0 | |
+-------------+----------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

Field :列名字
key :有沒有索引,索引型別
PRI: 主鍵索引(聚簇索引)
UNI: 唯一索引,唯一建unique
MUL: 輔助索引(單列,聯和,字首)

show index from city; # 檢視更具體的索引資訊

7.1 單列普通輔助索引

7.1.1 建立索引,刪除索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
### 新建索引
# 方式1
db01 [world]>alter table city add index idx_name(name);
表 索引名(列名)
# 方式2
db01 [world]>create index idx_name1 on city(name);
# 檢視索引
db01 [world]>show index from city;
# 注意:
以上操作不代表生產操作,我們不建議在一個列上建多個索引
同一個表中,索引名不能同名。

##### 刪除索引:
db01 [world]>alter table city drop index idx_name1;
表名 索引名

image-20200815160600443

7.2 覆蓋索引(聯合索引)

1
Master [world]>alter table city add index idx_co_po(countrycode,population);

7.3 字首索引

1
2
db01 [world]>alter table city add index idx_di(district(5));
注意:數字列不能用作字首索引。

7.4 唯一索引

1
2
db01 [world]>alter table city add unique index idx_uni1(name);
ERROR 1062 (23000): Duplicate entry 'San Jose' for key 'idx_uni1'

統計city表中,以省的名字為分組,統計組的個數

1
2
3
4
select district,count(id) from city group by district;
需求: 找到world下,city表中 name列有重複值的行,最後刪掉重複的行
db01 [world]>select name,count(id) as cid from city group by name having cid>1 order by cid desc;
db01 [world]>select * from city where name='suzhou';

7.5 檢視是否走索引

1
2
3
# explain select * from city where name ='shanghai';
# 圖一 type 是all 表示全表掃描
# 圖二 type 是ref 表示走了索引

image-20200815160906004image-20200815161030007

7.6 是否走索引壓測

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 1 建立資料庫test :create database test charset='utf8';
# 2 匯入100w條資料 source t100w.sql
# 3 執行:模仿100個使用者,同時查詢select * from test.t_100w where k2='780P',一共執行200次,平均一人兩次
mysqlslap --defaults-file=/etc/my.cnf --concurrency=100 --iterations=1 --create-schema='test' --query="select * from test.t100w where k2='780P'" engine=innodb --number-of-queries=200 -uroot -verbose

# 4 建立索引再測試:
alter table test.t100w add index idx_name(k2);
Benchmark
Running for engine rbose
Average number of seconds to run all queries: 0.084 seconds
Minimum number of seconds to run all queries: 0.084 seconds
Maximum number of seconds to run all queries: 0.084 seconds
Number of clients running queries: 100
Average number of queries per client: 2

# 5 刪除索引再測試
alter table test.t100w drop index idx_name;
Benchmark
Running for engine rbose
Average number of seconds to run all queries: 51.012 seconds
Minimum number of seconds to run all queries: 51.012 seconds
Maximum number of seconds to run all queries: 51.012 seconds
Number of clients running queries: 100
Average number of queries per client: 2

八 執行計劃獲取及分析

8.0 介紹

1
2
3
4
5
6
7
8
(1)
獲取到的是優化器選擇完成的,他認為代價最小的執行計劃.
作用: 語句執行前,先看執行計劃資訊,可以有效的防止效能較差的語句帶來的效能問題.
如果業務中出現了慢語句,我們也需要藉助此命令進行語句的評估,分析優化方案。
(2) select 獲取資料的方法
1. 全表掃描(應當儘量避免,因為效能低)
2. 索引掃描
3. 獲取不到資料

8.1 執行計劃獲取

獲取優化器選擇後的執行計劃

1
2
3
4
5
方式一:desc +sql 語句
desc select * from test.t100w;
方式二:explain +sel語句
explain select * from test.t100w;
explain select * from test.t100w\G;

8.2 執行計劃分析

8.2.0 重點關注的資訊

1
2
3
4
5
6
7
table: city                              # 查詢操作的表 (後期可能多表關聯查詢)
type:ref # 查詢型別 (全表,索引掃描)
possible_keys: CountryCode,idx_co_po # 可能會走的索引,執行計劃會有多種方案
key: CountryCode # 真正走的索引名字,最後優化器選擇的
key_len:null # 索引覆蓋長度
rows:997529 #查詢結果集的長度,此次查詢需要掃描的行數
Extra: Using index condition # 額外資訊

image-20200815173852417

1
2
3
4
5
desc select country.name,city.name from city join country on city.countrycode=country.code where city.population='CHN'\G;
# city 表沒有走索引,type 是all
# 優化一下,給populations欄位加索引
alter table city add index idx(population);
# 再看,就走索引了

image-20200815180329976

8.2.1 type詳解

8.2.1.1 簡介

1
2
3
type型別:all,index , range ,ref, eq_ref,const(system)
all:是全表掃描
index ,range ,ref,eq_ref,const(system)是索引掃描,但是順序從左向右,效率依次提高

8.2.1.2 ALL

1
2
3
4
5
6
7
######## 全表掃描的例子######## 
#1 ALL : 全表掃描,不走索引
desc select * from city;
desc select * from city where 1=1;
desc select * from city where countrycode not in ('chn','usa'); # not in不走索引,in走索引
desc select * from city where countrycode like '%ch%'; # like前後都加%,不走索引,構建索引要排序,前面是百分號,沒法排序,遵循最左字首,左側要確定
desc select * from city where countrycode !='usa'; # 不等於也會全文掃描

8.2.1.3 index

1
2
3
4
######## 索引掃描的例子######## 
# index < range <ref <eq_ref<const(system)
# 2 index:全索引掃描
desc select countrycode from world.city; # countrycode有索引,但是需要掃描整棵索引樹

8.2.1.4 range

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 3 range:索引範圍查詢
輔助索引> < >= <= LIKE IN OR
主鍵 <> NOT IN

desc select * from city where id <10;
desc select * from city where countrycode like 'CH%';
desc select * from city where countrycode in ('CHN','USA');
# 改寫後,變成ref
desc select * from city where countrycode ='CHN'
union all
select * from city where countrycode ='USA';

# 特殊情況,主鍵的不等於,not in 是range型別
desc select * from city where id !=10;# 做成了<10 and >10
desc select * from city where id not in (10,20);

8.2.1.5 ref

1
2
# 輔助索引等值查詢  name='er'的情況
desc select * from city where countrycode ='CHN'

8.2.1.6 eq_ref

1
2
3
4
5
# 多表連線中,非驅動表連線條件是主鍵或唯一鍵
# A join B on A.xx=B.yy

desc select country.name,city.name from city join country on city.countrycode=country.code where city.population='CHN'\G;
# 非驅動表使用了主鍵索引

8.2.1.7 const

1
2
唯一索引的等值查詢
DESC SELECT * FROM city WHERE id=10;

8.2.2 其他欄位解釋

8.2.2.1 possible_keys和key

1
2
possible_keys:可能會走的索引,所有和此次查詢有關的索引
key:此次查詢選擇的索引

8.2.2.2 key_len

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 聯合索引覆蓋長度
# 對於聯合索引:index(a,b,c),我們希望將來的查詢對聯合索引用的越充分越好
# key_len 可以幫我們判斷,此次查詢走了聯合索引的幾部分

# key_len計算:
select * from t1 where a = and b= or c=
上面語句完全使用聯合索引
key_len=a長度+b長度+c長度

##### 數字型別
not null約束 沒有not null 約束
tinyint 1 1+1
int 4 4+1
bigint 8 8+1
# key_len:
a列 int型別 not null ----》長度為4
a列 int型別 沒有非空約束 ----》長度為5

#### 字元型別:utf8 ----》一個字元最大佔3個位元組
not null約束 沒有not null 約束
char(10) 3*10 3*10+1
varchar(10) 3*10+2 3*10+2+1
# 選擇此列最大字元長度
b列 char(10) not null ---》30
b列 char(10) 沒有非空約束 ---》31
c列 varchar(10) not null ---》32
c列 varchar(10) 沒有非空約 ---》33


# 假設是utf8mb4格式,該如何算?
create table t1(
a int not null, 4
b int, 5
c char(10) not null, 40
d varchar(10) 43
)charset =utf8mb4
# index(a,b,c,d)
# 問:查詢中完全覆蓋到4列索引,key_len是多少? 92

# 測試:新建表,建立4列索引,
desc select * from t1 where a =1 and b=2 or c='a' and d='c'; 92
desc select * from t1 where a =1 ; 4
# 通過數字可以判斷是否完全走了索引

8.2.2.3 rows

1
# 評估查詢需要掃描的資料行數

8.2.2.4 extra

1
2
3
4
5
6
7
8
9
10
11
# 如果出現useing filesort:表示此次查詢使用到了檔案排序,說明在查詢中的排序操作(查詢語句中有如下語句,索引應用的不是特別合理):order by,group by ,distinct...
DESC SELECT * FROM city WHERE countrycode='CHN' ORDER BY population
# 可以看到使用了額外的排序

# 需要將countrycode和population建立聯合索引,再次查詢就沒有useing filesort了,在索引裡排好序了


結論:
1.當我們看到執行計劃extra位置出現filesort,說明由檔案排序出現
2.觀察需要排序(ORDER BY,GROUP BY ,DISTINCT )的條件,有沒有索引
3. 根據子句的執行順序,去建立聯合索引

8.2.3 explain(desc)使用場景(面試題)

1
2
3
4
5
6
7
8
9
10
11
12
題目意思:  我們公司業務慢,請你從資料庫的角度分析原因
1.mysql出現效能問題,我總結有兩種情況:
(1)應急性的慢:突然夯住
應急情況:資料庫hang(卡了,資源耗盡)
處理過程:
1.show processlist; 獲取到導致資料庫hang的語句
2. explain 分析SQL的執行計劃,有沒有走索引,索引的型別情況
3. 建索引,改語句
(2)一段時間慢(持續性的):
(1)記錄慢日誌slowlog,分析slowlog
(2)explain 分析SQL的執行計劃,有沒有走索引,索引的型別情況
(3)建索引,改語句

九 索引應用規範

1
2
3
4
5
業務
1.產品的功能
2.使用者的行為
"熱"查詢語句 --->較慢--->slowlog
"熱"資料

9.1 建立索引的原則(DBA運維規範)

9.1.0 說明

1
為了使索引的使用效率更高,在建立索引時,必須考慮在哪些欄位上建立索引和建立什麼型別的索引。那麼索引設計原則又是怎樣的?

9.1.1 (必須的) 建表時一定要有主鍵,一般是個無關列

1
一定要有主鍵,數字列最好,無關業務

9.1.2 選擇唯一性索引

1
2
3
4
5
6
7
8
9
10
11
唯一性索引的值是唯一的,可以更快速的通過該索引來確定某條記錄。
例如,學生表中學號是具有唯一性的欄位。為該欄位建立唯一性索引可以很快的確定某個學生的資訊。
如果使用姓名的話,可能存在同名現象,從而降低查詢速度。

優化方案:
(1) 如果非得使用重複值較多的列作為查詢條件(例如:男女),可以將表邏輯拆分
(2) 可以將此列和其他的查詢類,做聯和索引
(3) 聯合索引,要把重複值少的放在最左側
select count(*) from world.city;
select count(distinct countrycode) from world.city;
select count(distinct countrycode,population ) from world.city;

9.1.3(必須的) 為經常需要where 、ORDER BY、GROUP BY,join on等操作的欄位建立索引

1
2
3
4
5
6
7
8
排序操作會浪費很多時間。
where A B C ----》 A B C
in
where A group by B order by C
A,B,C

如果為其建立索引,優化查詢
注:如果經常作為條件的列,重複值特別多,可以建立聯合索引。

9.1.4 儘量使用字首來索引

1
如果索引欄位的值很長,最好使用值的字首來索引,減少索引樹高度

9.1.5 限制索引的數目

1
2
3
4
5
6
索引的數目不是越多越好。
可能會產生的問題:
(1) 每個索引都需要佔用磁碟空間,索引越多,需要的磁碟空間就越大。
(2) 修改表時,對索引的重構和更新很麻煩。越多的索引,會使更新表變得很浪費時間。
(3) 優化器的負擔會很重,有可能會影響到優化器的選擇.
percona-toolkit中有個工具,專門分析索引是否有用

9.1.6 刪除不再使用或者很少使用的索引(percona toolkit)

1
2
3
4
pt-duplicate-key-checker

表中的資料被大量更新,或者資料的使用方式被改變後,原有的一些索引可能不再需要。資料庫管理
員應當定期找出這些索引,將它們刪除,從而減少索引對更新操作的影響。

9.1.7 大表加索引,要在業務不繁忙期間操作

9.1.8 儘量少在經常更新值的列上建索引

9.1.9 建索引原則

1
2
3
4
5
6
(1) 必須要有主鍵,如果沒有可以做為主鍵條件的列,建立無關列
(2) 經常做為where條件列 order by group by join on, distinct 的條件(業務:產品功能+使用者行為)
(3) 最好使用唯一值多的列作為索引,如果索引列重複值較多,可以考慮使用聯合索引
(4) 列值長度較長的索引列,我們建議使用字首索引.
(5) 降低索引條目,一方面不要建立沒用索引,不常使用的索引清理,percona toolkit(xxxxx)
(6) 索引維護要避開業務繁忙期

9.2 不走索引的情況(開發規範)

9.2.1 沒有查詢條件,或者查詢條件沒有建立索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
select * from tab;       全表掃描。
select * from tab where 1=1;
在業務資料庫中,特別是資料量比較大的表。
是沒有全表掃描這種需求。
1、對使用者檢視是非常痛苦的。
2、對伺服器來講毀滅性的。
(1)
select * from tab;
SQL改寫成以下語句:
select * from tab order by price limit 10 ; 需要在price列上建立索引
(2)
select * from tab where name='zhangsan' name列沒有索引
改:
1、換成有索引的列作為查詢條件
2、將name列建立索引

9.2.2 查詢結果集是原表中的大部分資料,應該是25%以上。

1
2
3
4
5
6
7
8
查詢的結果集,超過了總數行數25%,優化器覺得就沒有必要走索引了。

假如:tab表 id,name id:1-100w ,id列有(輔助)索引
select * from tab where id>500000;
如果業務允許,可以使用limit控制。
怎麼改寫 ?
結合業務判斷,有沒有更好的方式。如果沒有更好的改寫方案
儘量不要在mysql存放這個資料了。放到redis裡面。

9.2.3 索引本身失效,統計資料不真實

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
索引有自我維護的能力。
對於表內容變化比較頻繁的情況下,統計資訊不準確,過舊,有可能會出現索引失效。
一般是刪除重建
# 統計資訊

現象:
有一條select語句平常查詢時很快,突然有一天很慢,會是什麼原因
select? --->索引失效,,統計資料不真實,大量修改,刪除性的操作
DML ? --->鎖衝突
解決:
重建索引,優化表

# 統計資訊放在了mysql資料庫的,資料改了,記錄的統計資訊不真實,會導致索引失效
innodb_index_stats
innodb_table_stats
select * from innodb_table_stats;
# 有哪個庫,哪個表,上次更新時間,資料行數,聚簇索引大小,輔助索引大小等
假設我們刪除一部分資料,這個記錄不是實時更新的
delete from city where id=100;
# 再檢視,行數不變,可以使用如下兩條命令:優化表
optimize table world.city;
alter table world.city engine=innodb;
# 再檢視就更新了

image-20200816162937142

9.2.4 查詢條件使用函式在索引列上,或者對索引列進行運算,運算包括(+,-,*,/,! 等)

1
2
3
4
5
6
例子:
錯誤的例子:select * from test where id-1=9;
正確的例子:select * from test where id=10;
算術運算
函式運算
子查詢

9.2.5 隱式轉換導致索引失效.這一點應當引起重視.也是開發中經常會犯的錯誤.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
這樣會導致索引失效. 錯誤的例子:
# 建立表
create table tab(id int,telnum char(11));
# 給telnum增加索引
mysql> alter table tab add index inx_tel(telnum);
# 檢視
mysql> desc tab;
# 查詢資料
mysql> select * from tab where telnum='1333333';
mysql> select * from tab where telnum=1333333;
# 分析
# 走索引
mysql> explain select * from tab where telnum='1333333';
# 不走索引(出現了隱士轉換,做了函式運算)
mysql> explain select * from tab where telnum=1555555;

9.2.6 <> ,not in 不走索引(輔助索引)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# <>  ,not in 不走索引,但是對於主鍵走range索引
EXPLAIN SELECT * FROM teltab WHERE telnum <> '110';
EXPLAIN SELECT * FROM teltab WHERE telnum NOT IN ('110','119');

mysql> select * from tab where telnum <> '1555555';
mysql> explain select * from tab where telnum <> '1555555';

單獨的>,<,in 有可能走,也有可能不走,和結果集有關(當查詢結果集超過25%,也會不走索引),儘量結合業務新增limit
or或in 儘量改成union
EXPLAIN SELECT * FROM teltab WHERE telnum IN ('110','119');
改寫成:
EXPLAIN SELECT * FROM teltab WHERE telnum='110'
UNION ALL
SELECT * FROM teltab WHERE telnum='119'

9.2.7 like “%_” 百分號在最前面不走

1
2
3
EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '31%'  走range索引掃描
EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '%110' 不走索引
%linux%類的搜尋需求,可以使用elasticsearch+mongodb 專門做搜尋服務的資料庫產品

十擴充套件:優化器針對索引的演算法

10.1 mysql索引的自優化-AHI

1
2
3
4
5
# 自適應雜湊索引:AHI,自動統計索引頁使用情況,記憶體中放在buffer pool中,可能會在記憶體回收的情況下,把經常使用的索引頁回收(置換)掉(這是我們不希望看到的),我們需要把熱的索引頁,生成一個hash表的型別,存到AHI中

# 自帶的,自優化能力

# 作用:自動評估 ’熱‘的記憶體索引page,生成hash索引表,幫助innodb快速讀取索引頁,加速索引讀取速度

10.2 mysql索引的自優化-Change buffer

1
2
3
4
5
6
7
8
9

比如insert,update,delete 資料
對於聚簇索引會立即更新
對於輔助索引,不是實時更新
在innodb記憶體結構中,加入了insert buffer(會話),現在的版本叫change buffer
change buffer的功能是臨時緩衝輔助索引需要的資料更新
當我們要查詢新insert的資料,會在記憶體中進行merge(合併)操作,此時輔助索引就是最新的

每個會話都分一個,可以調整,但是不能調太大

以上是(AHI,Change buffer)自優化能力,不需要單獨配置,下面的是優化演算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
show variables like '%switch%';
select @@optimizer_switch\G;
# 如下演算法
index_merge=on,
index_merge_union=on,
index_merge_sort_union=on,
index_merge_intersection=on,
engine_condition_pushdown=on,
index_condition_pushdown=on, # 索引下推
mrr=on,
mrr_cost_based=on, #
block_nested_loop=on, #
batched_key_access=off, #
materialization=on,
semijoin=on,
loosescan=on,
firstmatch=on,
duplicateweedout=on,
subquery_materialization_cost_based=on,
use_index_extensions=on,
condition_fanout_filter=on,
derived_merge=on

# 如何修改?
方式一:
配置檔案my.cnf
方式二:
set global optimizer_switch='index_condition_pushdown=on,mrr_cost_based=on';
set global optimizer_switch='batched_key_access=on';
# 重啟會話,退出重連
方式三:單獨給某個語句開
BKA hins方式
https://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-7.html

SELECT /*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY, f2_idx) */ f1
FROM t3 WHERE f1 > 30 AND f1 < 33;
SELECT /*+ BKA(t1) NO_BKA(t2) */ * FROM t1 INNER JOIN t2 WHERE ...;
SELECT /*+ NO_ICP(t1, t2) */ * FROM t1 INNER JOIN t2 WHERE ...;
EXPLAIN SELECT /*+ NO_ICP(t1) */ * FROM t1 WHERE ...;

10.3 ICP:索引下推

1
2
3
4
5
6
7
8
9
10
11
12
13
# 原理:
select 查詢語句在sql層解析後,由優化器選擇好方案,進入引擎層後,再由引擎層進行一次過濾,過濾好後再訪問硬碟的頁的資料
ICP是在引擎層又進行一次過濾,把索引優化的能力,下推到了引擎層
# 作用:
減少無關資料頁的掃描,最大程度使用索引,解決了聯合索引只能部分應用的情況
將不走索引的條件,在engine層取出資料之前做二次過濾
過濾掉一些無關資料

### 舉個例子
假設有索引:index(a,b,c) 、
查詢資料:select * from t1 where a= and c =
正常是在server層通過優化器優化,只能走a的索引,所以查a的資料走了索引,查c的資料還需要再全表掃描,這樣導致掃描資料量很大(a走索引,c在a的結果集上走全表)
通過ICP,把索引優化下推到引擎層,在引擎層再做一次過濾,得到更少量的資料,從而提高io速度(本來是要拿出滿足a條件的資料,然後在結果集上過濾c,現在拿出a的資料集之前再做一次過濾,資料集更少,然後再過濾c條件)

image-20200816172000718

沒有ICP的情況

1
server 層在做完索引優化以後,需要去磁碟上取4個數據頁(紅色的),但是實際上滿足條件的只有一個,沒有icp會多餘讀取3個沒用的資料頁

image-20200816172038812

有ICP的情況

1
server 層在做完索引優化以後,需要去磁碟上取4個數據頁(紅色的),但是實際上滿足條件的只有一個,到達engin層後,再做一次過濾,發現滿足條件的只有一個頁,所以,只取有用的那個頁(其實就是引擎層又加了一個判斷,減少無關資料頁的掃描)

image-20200816172233031

10.4 MRR-multi range read

1
2
3
4
5
6
7
8
9
10
mrr=on, # 開啟
mrr_cost_based=on, #關閉,是否通過cost base的方式來啟用MRR,由系統判斷是否值得,我們關閉
set global optimizer_switch='mrr=on,mrr_cost_based=off';

# 原理
範圍查詢 (大於,小於)
like查詢
有重複值
從輔助索引得到一個id值就要回表一次
在回表之前,先把id預存一下(緩衝區),排一下序(sort id),最後一次性回表(這樣有順序的就可以通過B+樹的neighbour直接順序取)

mrr之前

image-20200816184811892

mrr之後

image-20200816184902363

10.5 SNLJ

10.6 BNLJ

10.7 BKA

十一 問題彙總

11.1 怎樣減少回表

11.2 更新資料時,會對索引有影響嗎,資料的變化索引實時更新嗎?

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
insert delete一行資料
聚簇索引會立即更新
輔助索引不是實時更新
update 一行資料
看是不是更新輔助索引欄位(聚簇索引欄位不會改),輔助索引不會立即變化

# 補充
在InnoDB記憶體結構中(記憶體空間),加入了insert buffer(會話緩衝區),現在叫change buffer
原來主要針對insert操作,現在修改插入刪除都會走

1 聚簇索引,輔助索引,資料都在磁碟上存,innodb 存到ibd(表空間檔案:有段,區,頁)檔案中
2 當去查詢select * from t1 where name='zs',會把輔助索引的資料頁載入到記憶體(buffer pool)
3 回表,需要聚簇索引,也載入到記憶體中
4 新錄入資料,會更新聚簇索引,立即更新到磁碟
5 對於輔助索引,不是立即更新,先把變更放到change buffer(獨立記憶體區域)中,這樣磁碟上的輔助索引是舊資料
6 假設要讀新插入的一行,mysql會在記憶體中把change buffer中的變更的輔助索引和原來記憶體中的輔助索引merge(合併)一下,這個過程叫index merge(在記憶體中合併到一起)
7 這樣搜新插入的資料,是能搜到的
8 輔助索引沒有實時更新,減少了更新的頻次
9 當有查詢操作查詢這條資料後,輔助索引的資料會落到磁碟上(因為有查詢需求)
10 一旦涉及到更新磁碟,就會有一定程度的阻塞
11 每個會話(每個連結上來)都會有一個change buffer,大小可以調,通過調change buffer來優化大量的update和刪除等操作
12 當我們要查詢新insert的資料,會在記憶體中將輔助索引合併,這樣輔助索引就是最新的了(就是為了減少頻繁磁碟更新)