最全的 MySQL 8.0 新特性解讀

一、功能增強
1.1-所有系統表更換爲InnoDB引擎
系統表全部換成事務型的innodb表,默認的MySQL實例將不包含任何MyISAM表,除非手動創建MyISAM表。

1.2-DDL原子化
InnoDB表的DDL支持事務完整性,要麼成功要麼回滾,將DDL操作回滾日誌寫入到data dictionary 數據字典表 mysql.innodb_ddl_log 中用於回滾操作,該表是隱藏的表,通過show tables無法看到。通過設置參數,可將ddl操作日誌打印輸出到mysql錯誤日誌中。

mysql> set global log_error_verbosity=3;
mysql> set global innodb_print_ddl_logs=1;
1
2
1.3-DDL秒加列
只有在 MySQL 8.0.12 以上的版本才支持

mysql> show create table sbtest1;
CREATE TABLE `sbtest1` (
`id` int NOT NULL AUTO_INCREMENT,
`k` int NOT NULL DEFAULT '0',
`c` char(120) NOT NULL DEFAULT '',
`pad` char(60) NOT NULL DEFAULT '',
`d` int NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

mysql> alter table sbtest1 drop column d ;
Query OK, 0 rows affected (0.05 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql>
mysql> insert into sbtest1(k,c,pad) select k,c,pad from sbtest1;
Query OK, 1000000 rows affected (19.61 sec)
Records: 1000000 Duplicates: 0 Warnings: 0

mysql> insert into sbtest1(k,c,pad) select k,c,pad from sbtest1;
Query OK, 2000000 rows affected (38.25 sec)
Records: 2000000 Duplicates: 0 Warnings: 0

mysql> insert into sbtest1(k,c,pad) select k,c,pad from sbtest1;
Query OK, 4000000 rows affected (1 min 14.51 sec)
Records: 4000000 Duplicates: 0 Warnings: 0

mysql> select count(*) from sbtest1;
+----------+
| count(*) |
+----------+
| 8000000 |
+----------+
1 row in set (0.31 sec)

mysql> alter table sbtest1 add column d int not null default 0;
Query OK, 0 rows affected (1.22 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> alter table sbtest1 add column e int not null default 0;
Query OK, 0 rows affected (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 0


1.4-公用表表達式(CTE:Common Table Expression)
CTE(Common Table Expression)可以認爲是派生表(derived table)的替代,在一定程度上,CTE簡化了複雜的join查詢和子查詢,另外CTE可以很方便地實現遞歸查詢,提高了SQL的可讀性和執行性能。CTE是ANSI SQL 99標準的一部分,在MySQL 8.0.1版本被引入。

1.4.1-CTE優勢
查詢語句的可讀性更好

在一個查詢中,可以被引用多次

能夠鏈接多個CTE

能夠創建遞歸查詢

能夠提高SQL執行性能

能夠有效地替代視圖

1.5-默認字符集由latin1變爲utf8mb4
在8.0版本之前,默認字符集爲latin1,utf8指向的是utf8mb3,8.0版本默認字符集爲utf8mb4,utf8默認指向的也是utf8mb4。

1.6-Clone插件
MySQL 8.0 clone插件提供從一個實例克隆出另外一個實例的功能,克隆功能提供了更有效的方式來快速創建MySQL實例,搭建主從複製和組複製。

1.7-資源組
MySQL 8.0新增了一個資源組功能,用於調控線程優先級以及綁定CPU核。MySQL用戶需要有 RESOURCE_GROUP_ADMIN權限才能創建、修改、刪除資源組。在Linux環境下,MySQL進程需要有 CAP_SYS_NICE 權限才能使用資源組完整功能。

1.8-角色管理
角色可以認爲是一些權限的集合,爲用戶賦予統一的角色,權限的修改直接通過角色來進行,無需爲每個用戶單獨授權。

# 創建角色
mysql>create role role_test;
QueryOK, 0rows affected (0.03sec)

# 給角色授予權限
mysql>grant select on db.*to 'role_test';
QueryOK, 0rows affected (0.10sec)

# 創建用戶
mysql>create user 'read_user'@'%'identified by '123456';
QueryOK, 0rows affected (0.09sec)

# 給用戶賦予角色
mysql>grant 'role_test'to 'read_user'@'%';
QueryOK, 0rows affected (0.02sec)

# 給角色role_test增加insert權限
mysql>grant insert on db.*to 'role_test';
QueryOK, 0rows affected (0.08sec)

# 給角色role_test刪除insert權限
mysql>revoke insert on db.*from 'role_test';
QueryOK, 0rows affected (0.10sec)

# 查看默認角色信息
mysql>select * from mysql.default_roles;

# 查看角色與用戶關係
mysql>select * from mysql.role_edges;

# 刪除角色
mysql>drop role role_test;


1.9-多值索引
從 MySQL 8.0.17 開始,InnoDB 支持創建多值索引,這是在存儲值數組的 JSON 列上定義的二級索引,單個數據記錄可以有多個索引記錄。這樣的索引使用關鍵部分定義,例如 CAST(data->’$.zipcode’ AS UNSIGNED ARRAY)。 MySQL 優化器自動使用多值索引來進行合適的查詢,可以在 EXPLAIN 的輸出中查看。

1.10-函數索引
MySQL 8.0.13 以及更高版本支持函數索引(functional key parts),也就是將表達式的值作爲索引的內容,而不是列值或列值前綴。 將函數作爲索引鍵可以用於索引那些沒有在表中直接存儲的內容。

其實MySQL5.7中推出了虛擬列的功能,而MySQL8.0的函數索引也是依據虛擬列來實現的。

只有那些能夠用於計算列的函數才能夠用於創建函數索引。
函數索引中不允許使用子查詢、參數、變量、存儲函數以及自定義函數。
SPATIAL 索引和 FULLTEXT 索引不支持函數索引。
1.11-不可見索引
在MySQL 5.7版本及之前,只能通過顯式的方式刪除索引。此時,如果發現刪除索引後出現錯誤,又只能通過顯式創建索引的方式將刪除的索引創建回來。如果數據表中的數據量非常大,或者數據表本身比較大,這種操作就會消耗系統過多的資源,操作成本非常高。

從MySQL 8.x開始支持隱藏索引(invisible indexes),只需要將待刪除的索引設置爲隱藏索引,使查詢優化器不再使用這個索引(即使使用force index(強制使用索引),優化器也不會使用該索引), 確認將索引設置爲隱藏索引後系統不受任何響應,就可以徹底刪除索引。 這種通過先將索引設置爲隱藏索引,再刪除索引的方式就是軟刪除 。

mysql> show create table t1\G
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`c1` int DEFAULT NULL,
`c2` int DEFAULT NULL,
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
KEY `idx_c1` (`c1`) /*!80000 INVISIBLE */
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

# 不可見的情況下是不會走索引的,key=null
mysql> explain select * from t1 where c1=3;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 20.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

# 設置爲索引可見,
mysql> alter table t1 alter index idx_c1 visible;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> show create table t1\G
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`c1` int DEFAULT NULL,
`c2` int DEFAULT NULL,
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
KEY `idx_c1` (`c1`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

# 可以走索引,key=idx_c1
mysql> explain select * from t1 where c1=3;
+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+
| 1 | SIMPLE | t1 | NULL | ref | idx_c1 | idx_c1 | 5 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)


1.12-新增降序索引
MySQL在語法上很早就已經支持降序索引,但實際上創建的仍然是升序索引。從8.0開始,實際創建的爲降序索引。

1.13-SET_VAR 語法
在sql語法中增加SET_VAR語法,動態調整部分參數,有利於提升語句性能。

select /*+ SET_VAR(sort_buffer_size = 16M) */ id from test order id ;
insert /*+ SET_VAR(foreign_key_checks=OFF) */ into test(name) values(1);
1
2
1.14-參數修改持久化
MySQL 8.0版本支持在線修改全局參數並持久化,通過加上PERSIST關鍵字,可以將修改的參數持久化到新的配置文件(mysqld-auto.cnf)中,重啓MySQL時,可以從該配置文件獲取到最新的配置參數。

例如執行:

set PERSIST expire_logs_days=10 ;
1
系統會在數據目錄下生成一個包含json格式的 mysqld-auto.cnf 的文件,格式化後如下所示,當 my.cnf 和 mysqld-auto.cnf 同時存在時,後者具有更高優先級。

1.15-innodb select for update跳過鎖等待
select … for update,select … for share(8.0新增語法) 添加 NOWAIT、SKIP LOCKED語法,跳過鎖等待,或者跳過鎖定。

在5.7及之前的版本,select…for update,如果獲取不到鎖,會一直等待,直到innodb_lock_wait_timeout超時。

在8.0版本,通過添加nowait,skip locked語法,能夠立即返回。如果查詢的行已經加鎖,那麼nowait會立即報錯返回,而skip locked也會立即返回,只是返回的結果中不包含被鎖定的行。

1.16-group by 不再隱式排序
目的是爲了兼容sql的標準語法,方便遷移

mysql 5.7

mysql> select count(*),age from t5 group by age;
+----------+------+
| count(*) | age |
+----------+------+
| 1 | 25 |
| 1 | 29 |
| 1 | 32 |
| 1 | 33 |
| 1 | 35 |
+----------+------+
5 rows in set (0.00 sec)

mysql 8.0

mysql> select count(*),age from t5 group by age;
+----------+------+
| count(*) | age |
+----------+------+
| 1 | 25 |
| 1 | 32 |
| 1 | 35 |
| 1 | 29 |
| 1 | 33 |
+----------+------+
5 rows in set (0.00 sec)

可以看到,MySQL5.7 在group by中對分組字段進行了隱式排序,而MySQL8.0取消了隱式排序。如果要添加排序的話,需要顯示增加,比如 select count(*),age from t5 group by age order by age;

1.17-自增變量持久化
在8.0之前的版本,自增值是保存在內存中,自增主鍵AUTO_INCREMENT的值如果大於max(primary key)+1,在MySQL重啓後,會重置AUTO_INCREMENT=max(primary key)+1。這種現象在某些情況下會導致業務主鍵衝突或者其他難以發現的問題。自增主鍵重啓重置的問題很早就被發現(https://bugs.mysql.com/bug.php?id=199),一直到8.0才被解決。8.0版本將會對AUTO_INCREMENT值進行持久化,MySQL重啓後,該值將不會改變。

8.0開始,當前最大的自增計數器每當發生變化,值會被寫入redo log中,並在每個檢查點時候保存到private system table中。這一變化,對AUTO_INCREMENT值進行持久化,MySQL重啓後,該值將不會改變。

MySQL server重啓後不再取消AUTO_INCREMENT = N表選項的效果。如果將自增計數器初始化爲特定值,或者將自動遞增計數器值更改爲更大的值,新的值被持久化,即使服務器重啓。
在回滾操作之後立即重啓服務器將不再導致重新使用分配給回滾事務的自動遞增值。
如果將AUTO_INCREMEN列值修改爲大於當前最大自增值(例如,在更新操作中)的值,則新值將被持久化,隨後的插入操作將從新的、更大的值開始分配自動增量值。
-- 確認下自己的版本
select VERSION()
/*
VERSION() |
----------+
5.7.26-log|
*/

-- 創建表
create table testincr(
id int auto_increment primary key,
name varchar(50)
)

-- 插入數據
insert into testincr(name) values
('劉備'),
('關羽'),
('張飛');

-- 查看當前的自增量
select t.`AUTO_INCREMENT` from information_schema.TABLES t where TABLE_NAME ='testincr'
/*
AUTO_INCREMENT|
--------------+
4|
*/
-- 更改列值
update testincr set id=4 where id=3

-- 查看現在的表值
/*
id|name|
--+----+
1|劉備 |
2|關羽 |
4|張飛 |
*/
-- 插入新值 問題出現
insert into testincr(name) values('趙雲');
/*
SQL 錯誤 [1062] [23000]: Duplicate entry '4' for key 'PRIMARY'
*/

-- 如果我們再次插入,它就是正常的,因爲id到5了。。。
mysql> insert into testincr(name) values('趙雲');
Query OK, 1 row affected (0.01 sec)


1.18-binlog日誌事務壓縮
MySQL 8.0.20 版本增加了binlog日誌事務壓縮功能,將事務信息使用zstd算法進行壓縮,然後再寫入binlog日誌文件,這種被壓縮後的事務信息,在binlog中對應爲一個新的event類型,叫做Transaction_payload_event。

1.19-分區表改進
MySQL 8.0 對於分區表功能進行了較大的修改,在 8.0 之前,分區表在Server層實現,支持多種存儲引擎,從 8.0 版本開始,分區表功能移到引擎層實現,目前MySQL 8.0 版本只有InnoDB存儲引擎支持分區表。

1.20-自動參數設置
將innodb_dedicated_server開啓的時候,它可以自動的調整下面這四個參數的值:

innodb_buffer_pool_size 總內存大小
innodb_log_file_size redo文件大小
innodb_log_files_in_group redo文件數量
innodb_flush_method 數據刷新方法
1
2
3
4
只需將innodb_dedicated_server = ON 設置好,上面四個參數會自動調整,解決非專業人員安裝數據庫後默認初始化數據庫參數默認值偏低的問題,讓MySQL自適應的調整上面四個參數,前提是服務器是專用來給MySQL數據庫的,如果還有其他軟件或者資源或者多實例MySQL使用,不建議開啓該參數,本文以MySQL8.0.19爲例。

那麼按照什麼規則調整呢?MySQL官方給出了相關參數調整規則如下:

1. innodb_buffer_pool_size自動調整規則:

專用服務器內存大小 buffer_pool_size大小
小於1G 128MB (MySQL缺省值)
1G to 4G OS內存*0.5
大於4G OS內存*0.75
2. innodb_log_file_size自動調整規則:

buffer_pool_size大小 log_file_size 大小
小於8G 512MB
8G to 128G 1024MB
大於128G 2048MB
3. innodb_log_files_in_group自動調整規則:
(innodb_log_files_in_group值就是log file的數量)

buffer_pool_size大小 log file數量
小於8G ROUND(buffer pool size)
8G to 128G ROUND(buffer pool size * 0.75)
大於128G 64
說明:如果ROUND(buffer pool size)值小於2GB,那麼innodb_log_files_in_group會強制設置爲2。

4. innodb_flush_method自動調整規則:

該參數調整規則直接引用官方文檔的解釋:The flush method is set to O_DIRECT_NO_FSYNC when innodb_dedicated_server is enabled. If the O_DIRECT_NO_FSYNC setting is not available, the default innodb_flush_method setting is used.
如果系統允許設置爲O_DIRECT_NO_FSYNC;如果系統不允許,則設置爲InnoDB默認的Flush method。

1.20.1-自適應參數的好處:
自動調整,簡單方便,讓DBA更省心
自帶優化光環:沒有該參數前,innodb_buffer_pool_size和log_file_size默認安裝初始化後只有128M和48M,這對於一個生產環境來說是遠遠不夠的,通常DBA都會手工根據服務器的硬件配置來調整優化,該參數出現後基本上可以解決入門人員安裝MySQL後的性能問題。
雲廠商,虛擬化等動態資源擴容或者縮容後,不必再操心MySQL參數配置問題。
1.20.2-自適應參數的限制:
專門給MySQL獨立使用的服務器
單機多實例的情況不適用
服務器上還跑着其他軟件或應用的情況不適用
1.21-窗口函數
從 MySQL 8.0 開始,新增了一個叫窗口函數的概念。

什麼叫窗口?

它可以理解爲記錄集合,窗口函數也就是在滿足某種條件的記錄集合上執行的特殊函數。對於每條記錄都要在此窗口內執行函數,有的函數隨着記錄不同,窗口大小都是固定的,這種屬於靜態窗口;有的函數則相反,不同的記錄對應着不同的窗口,這種動態變化的窗口叫滑動窗口。

它可以用來實現若干新的查詢方式。窗口函數與 SUM()、COUNT() 這種聚合函數類似,但它不會將多行查詢結果合併爲一行,而是將結果放回多行當中。即窗口函數不需要 GROUP BY。

 

窗口函數內容太多,後期我會專門寫一篇文章介紹窗口函數

1.22-索引損壞標記
當遇到索引樹損壞時,InnoDB會在redo日誌中寫入一個損壞標誌,這會使損壞標誌安全崩潰。InnoDB還將內存損壞標誌數據寫入每個檢查點的私有系統表中。

在恢復的過程中,InnoDB會從這兩個位置讀取損壞標誌,併合並結果,然後將內存中的表和索引對象標記爲損壞。

1.23-InnoDB memcached插件
InnoDB memcached插件支持批量get操作(在一個memcached查詢中獲取多個鍵值對)和範圍查詢。減少客戶端和服務器之間的通信流量,在單個memcached查詢中獲取多個鍵、值對的功能可以提高讀取性能。

1.24-Online DDL
從 MySQL 8.0.12 開始(僅僅指InnoDB引擎),以下 ALTER TABLE 操作支持 ALGORITHM=INSTANT:

添加列。此功能也稱爲“即時添加列”。限制適用。
添加或刪除虛擬列。
添加或刪除列默認值。
修改 ENUM 或 SET 列的定義。
更改索引類型。
重命名錶。
Online DDL的好處:

支持 ALGORITHM=INSTANT 的操作只修改數據字典中的元數據。表上沒有元數據鎖,表數據不受影響,操作是即時的,並不會造成業務抖動。這在一些服務級別要求比較高(7*24)的系統中,是非常方便的。該特性是由騰訊遊戲DBA團隊貢獻的。

如果未明確指定,則支持它的操作默認使用 ALGORITHM=INSTANT。如果指定了 ALGORITHM=INSTANT 但不受支持,則操作會立即失敗並出現錯誤。需要注意的是,在 MySQL 8.0.29 之前,一列只能作爲表的最後一列添加。不支持將列添加到其他列中的任何其他位置。從 MySQL 8.0.29 開始,可以將即時添加的列添加到表中的任何位置。

1.25-EXPLAIN ANALYZE
Explain 是我們常用的查詢分析工具,可以對查詢語句的執行方式進行評估,給出很多有用的線索。但他僅僅是評估,不是實際的執行情況,比如結果中的 rows,可能和實際結果相差甚大。

Explain Analyze 是 MySQL 8 中提供的新工具,可貴之處在於可以給出實際執行情況。Explain Analyze 是一個查詢性能分析工具,可以詳細的顯示出 查詢語句執行過程中,都在哪兒花費了多少時間。Explain Analyze 會做出查詢計劃,並且會實際執行,以測量出查詢計劃中各個關鍵點的實際指標,例如耗時、條數,最後詳細的打印出來。

這項新功能建立在常規的EXPLAIN基礎之上,可以看作是MySQL 8.0之前添加的EXPLAIN FORMAT = TREE的擴展。EXPLAIN除了輸出查詢計劃和估計成本之外,EXPLAIN ANALYZE還會輸出執行計劃中各個迭代器的實際成本。

1.26-ReplicaSet
InnoDB ReplicaSet 由一個主節點和多個從節點構成. 可以使用MySQL Shell的ReplicaSet對象和AdminAPI操作管理複製集, 例如檢查InnoDB複製集的狀態, 並在發生故障時手動故障轉移到新的主服務器.

ReplicaSet 所有的節點必須基於GTID,並且數據複製採用異步的方式。使用複製集還可以接管既有的主從複製,但是需要注意,一旦被接管,只能通過AdminAPI對其進行管理。

1.27-備份鎖
在MySQL 8.0中,引入了一個輕量級的備份鎖,這個鎖可以保證備份一致性,而且阻塞的操作相對比較少,是一個非常重要的新特性。

在MySQL 8.0中,爲了解決備份FTWRL的問題,引入了輕量級的備份鎖;可以通過LOCK INSTANCE FOR BACKUP和UNLOCK INSTANCE,以獲取和釋放備份鎖,執行該語句需要BACKUP_ADMIN權限。

backup lock不會阻塞讀寫操作。不過,backup lock會阻塞大部分DDL操作,包括創建/刪除表、加/減字段、增/刪索引、optimize/analyze/repair table等。

總的來說,備份鎖還是非常實用的,畢竟其不會影響業務的正常讀寫;至於備份鎖和DDL操作的衝突,還是有很多方法可以避免,比如錯開備份和變更的時間、通過pt-online-schema-change/gh-ost避免長時間阻塞等等。隨着備份鎖的引入,Oracle官方備份工具MEB 8.0和Percona開源備份工具XtraBackup 8.0,也是更新了對backup lock的支持。

1.28-Binlog增強
MySQL 8.0.20 版本增加了binlog日誌事務壓縮功能,將事務信息使用zstd算法進行壓縮,然後再寫入binlog日誌文件,這種被壓縮後的事務信息,在binlog中對應爲一個新的event類型,叫做Transaction_payload_event。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章