目錄
19年10月14日MySQL發佈了8.0.18版本,GA穩定版,介紹一下MySQL8.0中出現的新玩意和新修改。
一,redo log重構
redo log是重做日誌,提供前滾操作,他是innodb事務日誌的一部分,另一部分是undo log,提供回滾操作。
redo log是一種物理日誌,記錄的是數據頁的物理修改,而undo是行記錄。
redo log分爲兩部分,一是寫入內存的日誌,二是從內存寫入磁盤的日誌。
在8.0版本以前,這兩部分日誌在寫入時都需要加鎖,也是性能瓶頸所在,而MySQL8.0版本去掉了這個鎖,採用異步操作,用戶線程只需要把日誌寫入緩存,刷盤的操作由其他的線程完成,提高了整體的運行效率。
二,原子DDL
簡潔的來看就是這樣,有一個sql:
DROP TABLE table1,table2;
如果刪除了table1之後服務掛了,
8.0之前:table1成功刪除,table2沒有刪除。非原子操作。
8.0之後:table1和table2都不會刪除。原子操作。
MySQL8.0的原子DDL包括了這些命令:
- DROP TABLES
- DROP SCHEMA
- CREATE TABLE
- TRUNCATE TABLE
- RENAME TABLES
- ALTER TABLE
8.0版本原子DDL的實現得益於MySQL臨時文件系統的調整,在DDL過程中可能用到的臨時文件都被整合到了一個文件中:
8.0之前:系統層的數據字典文件(.frm .trg .opt等),innodb層的元數據文件(表信息等),分開存儲,二者可能會出現不一致的情況。
8.0之後:都在innoDB的元數據文件中。DDL日誌寫入mysql.innodb_ddl_log表。
在此基礎上,Mysql把DDL操作中的數據字典修改,innoDB層的修改,寫binlog等操作合成了一個事務,保證了原子性。
暫時只有innoDB引擎支持原子DDL。
三,直方圖
MySQL中的直方圖實際上是對錶的某個字段數據分佈的統計,藉此可以向查詢優化器提供更精確的字段信息,幫助優化器得到更優的查詢計劃。
詳細介紹請看這一篇:
四,Hash Join
Hash Join可以在被驅動表沒有索引的情況下進行快速的連接並查詢。
大表連接時效果更好。
詳細介紹請看這一篇:
五,降序索引
降序索引出現之前:
MySQL8.0開始真正支持降序索引,之所以說是真正,是因爲相關的語法在之前的版本中就出現了,像這樣的:
ALTER TABLE `tableA` ADD INDEX `indexA` (`columnA ` desc) ;
但是實際上8.0之前MySQL會忽略這個desc的定義,依然創建一個普通的升序索引。
對於單個字段的索引,升序索引和降序索引其實差不多,因爲索引的數據結構是個B+樹,一個升序索引倒着查詢就能當降序索引用,效率比正着查低一點。
降序索引的意義:
引入降序索引後,降序查詢的效率和正序可以一致了。
降序索引的另一項重要意義在於聯合索引,可以應對一個sql中有的字段升序排列有的字段降序排列的場景。
舉個例子:
當我們建立一個包含兩個字段的聯合索引:
ALTER TABLE `tableA` ADD INDEX `indexA` (`columnA`, `columnB`) ;
顯然索引中的兩個字段都是升序索引,那麼我們使用下面的sql查詢:
select * from `tableA` order by `columnA` asc, `columnB` desc;
上面建立的索引會被用到,MySQL會給columnB做一個排序操作,優化器會在Extra字段中展示:
Using index; Using filesort。
所以,如果我們對columnA使用升序索引,columnB使用降序索引:
ALTER TABLE `tableA` ADD INDEX `indexA` (`columnA`, `columnB` desc) ;
再次執行上面的查詢語句,MySQL8.0就不會再進行排序了,explain之後的結果裏Extra字段也只會有:
Using index
注意:
暫時只有InnoDB引擎支持降序索引。
降序索引只能用於BTREE索引,不能用於Hash索引。
六,隱藏索引
所謂隱藏索引,意思就是建了索引之後可以設爲隱藏,不讓查詢優化器用。
設置隱藏索引和非隱藏索引使用的是INVISIBLE和VISIBLE關鍵字。
隱藏索引可以用來測試索引效率,比不斷新增刪除索引效率高的多。
主鍵索引不能被設爲隱藏。
當一個表沒有顯式定義主鍵索引,而且有一個NOT NULL的列有UNIQUE索引時,第一個這種UNIQUE索引不能被設爲隱藏索引:
CREATE TABLE t2 (
i INT NOT NULL,
j INT NOT NULL,
UNIQUE j_idx (j)
) ENGINE = InnoDB;
mysql> ALTER TABLE t2 ALTER INDEX j_idx INVISIBLE;
ERROR 3522 (HY000): A primary key index cannot be invisible.
把主鍵索引設爲隱藏索引時也會有這個提示。
上面的場景,先給表加個主鍵索引,然後這個UNIQUE索引就可以被設爲隱藏了。
建表語句中的sql是這樣的:
CREATE TABLE t1 (
i INT,
j INT,
k INT,
INDEX i_idx (i) INVISIBLE
) ENGINE = InnoDB;
CREATE INDEX j_idx ON t1 (j) INVISIBLE;
ALTER TABLE t1 ADD INDEX k_idx (k) INVISIBLE;
修改表語句中的sql是這樣的:
ALTER TABLE t1 ALTER INDEX i_idx INVISIBLE;
ALTER TABLE t1 ALTER INDEX i_idx VISIBLE;
查看索引是否是隱藏的,可以從INFORMATION_SCHEMA.STATISTICS表中獲取,或從SHOW INDEX獲取:
mysql> SELECT INDEX_NAME, IS_VISIBLE
FROM INFORMATION_SCHEMA.STATISTICS
WHERE TABLE_SCHEMA = 'db1' AND TABLE_NAME = 't1';
+------------+------------+
| INDEX_NAME | IS_VISIBLE |
+------------+------------+
| i_idx | YES |
| j_idx | NO |
| k_idx | NO |
+------------+------------+
系統變量optimizer_switch中有個use_invisible_indexes參數,默認爲off,當他被設置爲on時,即使索引被設爲隱藏的,優化器也會使用這個索引。
七,InnoDB讀鎖優化
MySQL8.0的讀鎖優化涉及兩組概念:
1,SELECT FOR SHARE和SELECT FOR UPDATE
2,NOWAIT 和SKIP LOCKED
下面分別介紹。
SELECT FOR SHARE和SELECT FOR UPDATE
SELECT FOR SHARE是共享鎖的查詢,此查詢的行不可以被別的事務修改,但是別的事務可以查詢這些行。這種寫法可以替換之前的SELECT LOCK IN SHARE MODE,其實功能是一樣的,不過這種查詢可以和NOWAIT 和SKIP LOCKED一起使用。
SELECT FOR UPDATE是排他鎖的查詢,此查詢的行不可以被別的事務查詢或修改。這種語法在之前的MySQL版本中就存在。這種查詢可以和NOWAIT 和SKIP LOCKED一起使用。
注:
如果子查詢的中沒寫SELECT FOR UPDATE,那麼子查詢中的行不會被鎖,比如:
SELECT * FROM t1 WHERE c1 = (SELECT c1 FROM t2) FOR UPDATE;
這個sql中t2表的行不會被鎖。
NOWAIT 和SKIP LOCKED
MySQL8.0新增的語法,這兩種語法是和SELECT FOR SHARE或SELECT FOR UPDATE一起使用的。
在不NOWAIT 和SKIP LOCKED的情況下,被SELECT FOR SHARE或SELECT FOR UPDATE鎖定的行是不能被其他事務用SELECT FOR SHARE或SELECT FOR UPDATE語法再次鎖定的,而NOWAIT 和SKIP LOCKED這兩種語法提供了其他的解決方案:
NOWAIT :如果查詢結果中有被鎖定的行,則直接返回錯誤。此方法不存在鎖的等待。
SKIP LOCKED,如果查詢結果中有被鎖定的行,則忽略這些被鎖定的行,返回那些沒被鎖定的行。此方法不存在鎖的等待。
注:
NOWAIT 和SKIP LOCKED只對行級鎖有效。
八,窗口函數
MySQL8.0引入的窗口函數,可以比較方便的實現一些分析和統計功能,這些功能不用窗口函數也能實現,不過可能sql會比較複雜。
詳細介紹請見此文:
九,新的優化器提示 SET_VAR
MySQL8.0.3加入的SET_VAR,是一種新的優化器提示,可以在一條sql中修改session級的系統變量,sql執行完後系統變量就會恢復。
這是一種註釋模式的優化器提示,這種模式在註釋中的第一個字符必須是加號+。
如果我們只想在一個sql中使用不同的系統變量值,在以前是這樣執行的:
SET @old_optimizer_switch = @@optimizer_switch;
SET optimizer_switch = 'mrr_cost_based=off';
SELECT * FROM t1;
SET optimizer_switch = @old_optimizer_switch;
使用SET_VAR就是這樣的:
SELECT
/*+ SET_VAR(optimizer_switch = 'mrr_cost_based=off') */ *
FROM t1;
顯然sql簡潔了很多。
可以同時修改多個系統變量的值:
SELECT
/*+ SET_VAR(optimizer_switch = 'mrr_cost_based=off')
SET_VAR(max_heap_table_size = 1G) */ *
FROM t1;
不是所有的系統變量都是可以在這裏面修改的,目前支持的變量有:
- auto_increment_increment
- auto_increment_offset
- big_tables
- bulk_insert_buffer_size
- default_tmp_storage_engine
- div_precision_increment
- end_markers_in_json
- eq_range_index_dive_limit
- foreign_key_checks
- group_concat_max_len
- insert_id
- internal_tmp_mem_storage_engine
- join_buffer_size
- lock_wait_timeout
- max_error_count
- max_execution_time
- max_heap_table_size
- max_join_size
- max_length_for_sort_data
- max_points_in_geometry
- max_seeks_for_key
- max_sort_length
- optimizer_prune_level
- optimizer_search_depth variables
- optimizer_switch
- range_alloc_block_size
- range_optimizer_max_mem_size
- read_buffer_size
- read_rnd_buffer_size
- sort_buffer_size
- sql_auto_is_null
- sql_big_selects
- sql_buffer_result
- sql_mode
- sql_safe_updates
- sql_select_limit
- timestamp
- tmp_table_size
- updatable_views_with_limit
- unique_checks
- windowing_use_high_precision
十,字符集優化
MySQL8.0開始支持Unicode9.0。
Unicode9.0新增支持了7500中新字符,包括72種emoji表情。另外還支持了一些小語種,其中一種是中國古代的西夏語。
新增的emoji表情:
MySQL8.0的默認字符集改爲utf8mb4。可以放心大膽的用emoji表情了。
十一,binlog複製優化
8.0版本使用WriteSet,對binlog的複製進行了優化,效率得以提高。
詳細介紹請見此文:
MySQL8.0新特性學習筆記(一):binlog複製策略優化
十二,持久化AUTO_INCREMENT值
在8.0版本之前,表的AUTO_INCREMENT值是存在內存中的,沒有持久化到磁盤中,所以會出現這樣的情況:
1,向表中插入3條記錄,不指定id,則id自增爲1,2,3。
2,刪除id爲3的行。
3,重啓MySQL服務。
4,向表中插入一條記錄,不指定id,則新增的這條記錄的id將會是3,和之前一條記錄的id重複。
這種id會重複的情況被記錄在bug#199下,有些情況下這個bug還是很煩人的,比如把主鍵當業務主鍵的場景(業務主鍵理論上不應該重複),或者數據歸檔的場景:
1,向表A中插入3條記錄,不指定id,則id自增爲1,2,3。
2,把表A的數據整體歸檔到表B,於是表A中無數據,表B中有3條數據。
3,重啓MySQL服務。
4,再向表A中插入一條記錄,id就會是1。於是這個表A以後也別想再歸檔了。
MySQL8.0版本修復了這個bug,表的AUTO_INCREMENT被持久化了,任意時間MySQL服務重啓,AUTO_INCREMENT值都會正確的加1。
十三,JSON語法
8.0版本開始正式支持JSON相關的語法,提供了JSON格式的數據庫字段類型,制定了JSON格式相關的規則,添加了大量的JSON相關的函數。
詳細介紹請見此文:
MySQL8.0新特性學習筆記(五):JSON格式簡介和JSON函數詳解
除了上面提到的修改和功能之外,MySQL8.0版本還有很多的修改,優化,和bug修復,以後慢慢學習。
完
MySQL8.0全部學習筆記:
MySQL8.0新特性學習筆記(一):binlog複製策略優化