MySQL8.0新特性學習筆記(六):新特性介紹

目錄

一,redo log重構

二,原子DDL

三,直方圖

四,Hash Join

五,降序索引

六,隱藏索引

七,InnoDB讀鎖優化

八,窗口函數

九,新的優化器提示 SET_VAR

十,字符集優化

十一,binlog複製優化

十二,持久化AUTO_INCREMENT值

十三,JSON語法


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中的直方圖實際上是對錶的某個字段數據分佈的統計,藉此可以向查詢優化器提供更精確的字段信息,幫助優化器得到更優的查詢計劃。

詳細介紹請看這一篇:

MySQL8.0新特性學習筆記(三):直方圖

 

四,Hash Join

Hash Join可以在被驅動表沒有索引的情況下進行快速的連接並查詢。

大表連接時效果更好。

詳細介紹請看這一篇:

MySQL8.0新特性學習筆記(四): 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會比較複雜。

詳細介紹請見此文:

MySQL8.0新特性學習筆記(二):窗口函數

 

九,新的優化器提示 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複製策略優化

MySQL8.0新特性學習筆記(二):窗口函數

MySQL8.0新特性學習筆記(三):直方圖

MySQL8.0新特性學習筆記(四):Hash Join

MySQL8.0新特性學習筆記(五):JSON格式簡介和JSON函數詳解

MySQL8.0新特性學習筆記(六):新特性介紹

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