MySQL與MariaDB核心特性比較詳細版v1.0,Oracle ACE主編(覆蓋mysql 8.0/mariadb 10.3,包括優化、功能及維護)

注:本文嚴禁任何形式的轉載,原文使用word編寫,爲了大家閱讀方便,提供pdf版下載。

MySQL與MariaDB主要特性比較詳細版v1.0(不含HA).pdf

鏈接:https://pan.baidu.com/s/1qAcrxg8eRumRi3FTJtXZxw
提取碼:giei

MySQL與MariaDB主要特性核心正式版v1.0.pdf

鏈接:https://pan.baidu.com/s/1yAKU7GIV4LDQRCvnx7oEnQ
提取碼:1d6t 

簡介

如果你希望編寫的SQL是非常通用的,不使用任何具體平臺相關的特性,那麼使用MySQL社區版、Percona Server或MariaDB並無本質性差別。所以,通常使用某個分支是有着特定考慮的,它們可能和高可用、SQL語法、亦或性能相關。本文討論的就是這些差別。

因爲percona server是直接fork Mysql社區版的,所以絕大多數服務器本身的特性是一樣的(除了線程池、備份、審計、InnoDB增強、及其在PMM中的一些監控工具,這些特性企業版MySQL也都有相應的提供),所以除非特別說明,在本文中針對mysql說明的特性基本上可以無縫適用於percona server,並不專門分開討論它們。由於實際專業用戶幾乎不會使用mysql社區版(但是在這裏,筆者還是要友情提醒下,mysql社區版的併發性和擴展性是較差的,如果生產系統用的是mysql社區版,應儘快切換爲percona server或mariadb,要查看percona server對mysql社區版的增強,可以參考https://learn.percona.com/download-percona-server-8-0-manual),所以大部分測試以percona server爲例,但是爲了溝通更方便,通常爲mysql。

有些讀者可能希望現在就知道筆者推薦使用哪個版本及其理由,就當前來說,mysql 5.7和mariadb 10.x之間,筆者建議使用mariadb 10.x。mysql 8.0和mariadb 10.x之間,還沒有足夠的依據讓筆者做出選擇,這幾個版本,本文都會詳細討論它們的差別。有些讀者會說阿里推薦使用percona server,應該來說阿里哪個版本都有在用,對於不同的時間、不同的用戶他們會推薦不同的版本,例如阿里雲上你可以同時找到MySQL版(https://www.aliyun.com/product/rds/mysql?spm=5176.12145306.1240834.s22.627e5022kzBGRY)和MariaDB版本(https://www.aliyun.com/product/rds/mariadb?spm=5176.7920929.selected.28.6f7741d6nFpaZk)。在此筆者還想補充一句,幾年前,對於B端系統,阿里雲也開始推薦PostgreSQL了,這確實是更適合的方案,但是這個推薦是在阿里雲上線postgresql之後(2015年,阿里雲宣佈正式推出RDS for PostgreSQL服務),14年筆者在公司論壇的帖子也是這麼說的。所以存在即合理,只是應該根據實際情況選擇儘可能最滿足要求的那個。

限於篇幅,本文只介紹筆者認爲比較重要的那些特性,對於可能有差別但是個人認爲並不會有很多成本差異的特性或並不太合適在數據庫中實現的特性,文本並不做討論;對於一些對開發或性能並沒有那麼重要的特性例如MySQL Shell、密鑰管理、默認身份認證等亦如是;第三,文本也不會討論MySQL和MariaDB在實現底層細節上的差異,例如對於子查詢中的order by,mysql會照實進行排序,但是mariadb會擇機優化去掉排序,它確實對於分析性能有幫助,但不是本文核心。在版本上,筆者主要參考MySQL5.7/8.0和MariaDB 10.3爲主,必要的時候也會提及之前的版本。

MySQL分支簡介

分支發展史

我們先來看一下MySQL的分支發展史,如下圖所示:

 

從上可知,整體而言,mysql和mariadb版本的一般比較如下:

l  10.3-10.4和8.0對應

l  10.1-3和5.7對應

l  10.0-1和5.6對應

mysql 5.7以及之前的大多數特性合併到了mariadb,但是mysql 8.0沒有合併(因爲MyISAM存儲引擎廢棄,提升了性能,提高了崩潰安全性,但mariadb並沒有兼容(筆者沒有細細分析,不排除是因爲開源協議的限制,導致mariadb無法使用mysql 8.0的內部數據字典,而只能使用5.7),這導致了文件級別的不兼容性)。

mariadb差不多從mysql 5.1版本的時候衍生出,但是mariadb第一個歷史性版本應該算是5.3,該版本引入了很多mysql 5.5不支持的特性,例如子查詢半連接優化、ICP、內嵌視圖合併、哈希連接等,但是該版本的功能層面特性新增並不多。

目前mysql 8.0和10.4的生產用戶應該來說比較少,因爲老司機一般根據mysql的套路會選擇a.b.20+版本,mariadb則a.b.10+的版本,這兩個小版本之後,相對就比較穩定了。所以這兩個版本估計廣泛被採用還要一段時間,而且因爲mysql 8.0之後系統表的存儲引擎從MyISAM換成了專有格式,後面MySQL和MariaDB的不一致性會越來越多,用戶從這個版本開始分支的選擇相比之前版本應該會更加謹慎。

    至於percona server,該分支本身並不提供對mysql功能的完善或加強,更多的是完善可靠性、擴展性、維護性,有些類似於CDH之於apache hadoop生態的關係。

主流維護版本

首先來看mysql和mariadb當前的各自活躍版本。mariadb目前主流的版本爲5.5到10.4,mariadb 10.4在6月份剛剛發佈GA版本,相比之前MariaDB 10.1-10.3這幾個大版本來說,這個版本並沒有包含特別多很有價值的新特性,唯一本身很有價值的特性時即時刪除列,但是該特性在實際中應該較少被用到,否則就說明設計存在問題(讀者有興趣可以參考https://mariadb.com/kb/en/library/changes-improvements-in-mariadb-104/)。mariadb正常大約一到兩個月發佈一次。

mysql的主流版本爲MySQL 5.6-8.0,大約3個月左右發佈一次,5.5已經半年多沒有發版了,https://dev.mysql.com/doc/relnotes/mysql/5.5/en/news-5-5-62.html

通常來說,MariaDB 的發佈頻率比 MySQL 更頻繁,從上可知,mariadb的發佈頻率大約比mysql快一倍。一般來說,太高的發佈頻率既有利也有弊。從好的方面來說,用戶可以更及時地收到功能和錯誤修復。從不好的方面來說,爲了讓 MariaDB 保持最新的狀態,很可能引入更多的bug。但是mariadb有一定的特殊性,因爲mariadb的創始人也是Mysql創始人,oracle收購sun之後,他另立門戶的目的就是再搞一個高度兼容mysql二進制和SQL接口、可直接替換mysql的分支。所以,它是否符合其他開源產品一樣更活躍是爲了更快的支持社區發展呢?目前來說確實大多數特性都快mysql一步,且即使mysql先支持的或後支持但有意不採用mariadb當前語法的,maraidb幾乎都做了兼容,例如虛擬列特性。否,mariadb也不見的是無私的,它的創始人讀者可能都瞭解,是mysql的創始人。不過至少到現在爲止,還看不出mariadb有postgresql和mongodb的套路。

安裝方式差別

從5.7版本開始(5.7仍然兼容老的模式),mysql和mariadb在mysql數據庫初始化方式上發生了變化,mysql 5.6以及之前的版本採用scripts/mysql_install_db進行數據庫實例初始化,從5.7開始,該方式被標記爲已過期,推薦採用mysqld --initialize進行初始化。

mariadb則仍然沿用5.6以及之前版本的方式,採用mysql_install_db進行安裝,新的mysqld --initialize方式暫不被支持。

MySQL與MariaDB關鍵特性比較

注:本來筆者的初衷是以計劃以10.3爲主要版本進行比較的,但是實際編寫過程中發現這個比較並不一定很恰當,很多特性在10.2甚至10.1就已經支持,這部分的比例還不小(相對於MySQL 8.0與之前的版本涇渭分明而言)。所以在最後將其標題的10.3去掉,並儘可能的描述了討論到的特性是哪個版本開始支持的。

mariadb官方比較完整的羅列了oracle mysql和mariadb兼容性、不兼容性以及系統參數上的差異,讀者可以參見https://mariadb.com/kb/en/library/compatibility-differences/,但是其更多的是參考手冊的性質,並沒有對特性做重要性劃分。

存儲引擎支持

MariaDB 比 MySQL 支持更多的存儲引擎類型。但話說回來,數據庫可以支持多少個存儲引擎並不重要,重要的是哪個數據庫可以支持適合你需求的存儲引擎。例如關心的是InnoDB及其增強、ColumnStore(mariadb從10.1開始支持,但是穩定比較晚)、Connect、MyRocks。

MariaDB支持的存儲引擎包括:XtraDB, InnoDB, MariaDB ColumnStore, Aria, Archive, Blackhole, Cassandra Storage Engine, Connect, CSV, FederatedX, Memory storage engine, Merge, Mroonga, MyISAM, MyRocks, QQGraph, Sequence Storage Engine, SphinxSE, Spider, TokuDB。

注:MariaDB 10.2開始從Percona XtraDB切換回InnoDB,其解釋是從Mysql 5.7開始,innodb基本上都包含了xtradb所做的改進,所以沒有必要使用xtradb引擎。

MySQL支持的存儲引擎包括:InnoDB, MyISAM, Memory, CSV, Archive, Blackhole, Merge, Federated, Example。

percona server新增了存儲引擎MyRocks:基於RocksDB(一個通過更好的壓縮實現閃存模式下更高性能的數據庫),對NVME SSD做了性能優化,Percona鼓勵TokuDB用戶探索MyRocks存儲引擎,它可以爲大多數工作負載提供類似的優勢,並且可以更好地優化對現代硬件的支持。

 

GTID

mysql 5.6和mariadb 10.0引入了GTID(全局事務ID)特性,它的目的在於使用GtidMysql能夠在整個複製環境中能夠自動的切換,而不像以前需要指定文件和位置。但是mysql和mariadb的實現並不兼容。mysql的gtid是服務器UUID+序列號組成,mariadb的GTID則是Domain ID+服務器ID+序列號組成。mariadb爲了儘可能兼容mysql,實現了對mysql GTID的部分兼容,使得從mysql同步到mariadb的GTID能夠被識別,反之不然。就GTID而言,影響比較大的是第三方的同步中間件例如otter/canal,對應用而言影響比較小。

線程池

mysql社區版並不包含線程池特性,如果應用有大量的數據庫短連接(例如成百上千連接不停的經常斷開、重連),線程池對於保持mysql數據庫穩定是有價值的,這也是早期不少用戶不選擇mysql社區版的主要原因。如果都是長連接或者連接並不是很多,則線程池的價值並沒有其所述的那麼大。mariadb和percona server都包含了線程池特性。

注:percona server的線程池是基於mariadb的線程池。

Oracle兼容性

在10.3以及之後的版本中,maraidb在原來的基礎上,有意增加了對多種數據庫語法的兼容,包括SQL_MODE=ORACLE、MSSQL,當設置SQL_MODE爲ORACLE時,相當於設置瞭如下選項:

SET SQL_MODE='PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT';

設置爲Oracle模式後,相關存儲過程和函數的定義,遊標、循環、變量賦值、異常、SQL類型、Begin塊這些基本特性就很大程度上兼容了Oracle PL/SQL的常規語法,對於特殊特性例如BUCK COLLECT INTO這些涉及到底層引擎的優化實現則基本上不在範圍,完整的支持特性可以參考https://mariadb.com/kb/en/library/sql_modeoracle-from-mariadb-103/

注:該特性雖然並非沒有價值,但是並不建議使用這個模式,爲了達到較好的可靠性和性能效果,純粹爲了一些語法糖並不值得讓去更換髮行版,更何況是分支。

匿名塊

從mariadb 10.1.1開始引入了對匿名塊的支持,如下所示:

DELIMITER $$

BEGIN NOT ATOMIC

IF 1=1 THEN

     SELECT * FROM assets;

ELSE

     SELECT * FROM t;

END IF;

END$$

item_name        dynamic_cols                    

---------------  ---------------------------------

MariaDB T-shirt 

Thinkpad Laptop 

當SQL_MODE=ORACLE(mariadb 10.3+)的時候,mariadb還支持oracle風格的匿名塊。這一特性的價值在於原來在給mysql做升級腳本時,不得不先定義一個存儲過程,調用、然後刪除,類似如下:

DROP PROCEDURE IF EXISTS sp_db_mysql;

DELIMITER $$

         CREATE PROCEDURE sp_db_mysql()

                   BEGIN

                            declare v_rowcount int;

                            declare database_name VARCHAR(100);

                            select database() into database_name;

                            select count(1) into v_rowcount from information_schema.columns where table_schema= database_name and table_name=’table_name’ and column_name='dict_name';

                            if v_rowcount = 1 then

                                     ALTER TABLE hs_tabase.sys_show_component MODIFY COLUMN dict_name varchar(2000) DEFAULT ' ';

                            end if;

                   END$$

DELIMITER ;

 

call sp_db_mysql();

DROP PROCEDURE IF EXISTS sp_db_mysql;

現在則幾乎完全可以和oracle一樣處理了,也就是不需要NOT ATOMIC子句,無論維護還是開發、測試都更加的簡便。

序列

MariaDB從10.3版本開始增加了對序列的支持。該特性可以解決多個字段無法共用一個序列的問題(注:該特性不要求設置sql_mode=oracle)。

CREATE SEQUENCE s START WITH 100 INCREMENT BY 10; -- 不要求SQL_MODE=ORACLE

SELECT PREVIOUS VALUE FOR s; -- sequence_name.currval oracle模式可以使用oracle序列的語法糖

SELECT NEXT VALUE FOR s; -- sequence_name.nextval oracle模式可以使用oracle序列的語法糖

因爲mariadb從10.2版本開始已經實現了持久化ID,所以不需要藉助該特性解決mysql自增列清空後又從0開始的問題,見持久性自增ID一節。序列還可以直接用於表的值,如下:

CREATE SEQUENCE s;

CREATE TABLE t(id INT);

INSERT INTO t VALUES(NEXT VALUE FOR s );

INSERT INTO t SELECT NEXT VALUE FOR s FROM t;

 

Oracle風格的動態SQL

mariadb 10.2開始增加了對oracle風格動態sql EXECUTE IMMEDIATE的支持。原來執行動態sql需要prepare,execute,deallocate三步,如下所示:

prepare stmt from "select 1";

execute stmt;

deallocate prepare stmt;

 

使用oracle風格的動態SQL將更加簡潔,如下:

EXECUTE IMMEDIATE 'SELECT 1'

 

EXECUTE IMMEDIATE一樣支持參數化方式執行,如下:

EXECUTE IMMEDIATE CONCAT('SELECT COUNT(*) FROM ', 't1', ' WHERE a=?') USING 5+5;

列默認值支持表達式和函數

從mariadb 10.2、mysql 8.0開始,支持將表達式和函數作爲列的默認值,這個特性還是很有價值的,尤其是對於日期相關的類型來說,有時候因爲某些原因需要使用date之外的類型,如果不支持表達式或函數,就比較麻煩了。如下所示:

CREATE TABLE tx (a varchar(32) DEFAULT (DATE_FORMAT(now(),"%Y-%m-%d %T")), b int DEFAULT 1);

insert into tx (b) values (2);

select * from tx;

參見:https://mariadb.com/kb/en/library/create-table/#default-column-option

10.3開始支持序列作爲默認值一部分,如下所示:

ALTER TABLE Observation MODIFY Id int(11) NOT NULL DEFAULT NEXT VALUE FOR Seq1_1;

CHECK約束

在mariadb 10.2以及mysql 8.0之前,check約束僅僅是語法上不報錯,並未真正生效,從這兩個版本開始,check約束真正生效。如下所示:

create table t1 (a int check(a>0) ,b int check (b> 0), constraint abc check (a>b));

insert into t1 VALUES(1,-1);

在mariadb下執行會報錯誤” [Err] 4025 - CONSTRAINT `t1.b` failed for `hs_tabase`.`t1`”,mysql則報” [Err] 3819 - Check constraint 't1_chk_2' is violated.(mysql)”。

持久性自增ID

mysql> create table t1(id int auto_increment primary key);

Query OK, 0 rows affected (0.01 sec)

 

mysql> insert into t1 values(null),(null),(null);

Query OK, 3 rows affected (0.01 sec)

Records: 3 Duplicates: 0 Warnings: 0

 

mysql> select * from t1;

+----+

| id |

+----+

| 1 |

| 2 |

| 3 |

+----+

rows in set (0.00 sec)

 

mysql> delete from t1 where id=3;

Query OK, 1 row affected (0.36 sec)

 

mysql> insert into t1 values(null);

Query OK, 1 row affected (0.35 sec)

 

mysql> select * from t1;

+----+

| id |

+----+

| 1 |

| 2 |

| 4 |

+----+

rows in set (0.01 sec)

mysql> delete from t1 where id=4;

# service mysqld restart

mysql> insert into t1 values(null);

Query OK, 1 row affected (0.00 sec)

 此時,在mysql 5.7以及之前的版本中,插入的值是3。在mysql 8.0版本中,插入的值是5。

mysql> select * from t1;

+----+

| id |

+----+

| 1 |

| 2 |

| 3 |

+----+

rows in set (0.00 sec)

mariadb在10.2.4靜悄悄的實現了該特性,因此該值也是5。https://jira.mariadb.org/browse/MDEV-6076

DDL WAIT/NOWAIT子句

默認情況下,DML語句是阻塞的,也就是當DML操作的記錄正在被其它會話更新時,當前會話會一直等待,直到超過lock_wait_timeout和innodb_lock_wait_timeout時間。有時候全局控制並不足夠靈活,在mariadb 10.3開始,Mariadb引入了wait N/nowait子句,允許用戶更精確地控制具體語句等待的時間,默認和原來一樣,等待模式。

A會話

SET autocommit=off;

SELECT * FROM t FOR UPDATE;

B會話

SET autocommit=off;

SELECT * FROM t FOR UPDATE nowait;

默認情況下,也就是不帶NOWAIT子句的時候,B會話會一直等待。

C會話

SET autocommit=off;

SELECT * FROM t FOR UPDATE nowait;

錯誤代碼: 1205

Lock wait timeout exceeded; try restarting transaction

除了NOWAIT,也支持等待N秒。如下:

SELECT * FROM t FOR UPDATE wait 3;

到目前爲止,mysql並沒有支持該特性。

完整的語法可以參考https://mariadb.com/kb/en/library/wait-and-nowait/

持久化更改參數SET PERSIST

mysql 8.0爲了修改系統參數引入了一個額外的選項,用於控制被修改系統參數的生效時間,只要是全局可修改且非只讀的變量都可以使用PERSIST_ONLY/PERSIST選項控制修改範圍,後者是前者和GLOBAL的組合。類似於oracle的alter system set var=value scope=memory|both|spfile。

SET PERSIST操作在performance_schema.persisted_variables表中讀取和設置持久變量列表。

SET PERSIST_ONLY max_connections=100;

SELECT * FROM `persisted_variables`;

持久化變量在datadir中存儲在mysqld-auto.cnf中,其使用JSON格式存儲。它包含的信息不僅僅是持久值,還包括諸如誰做出改變以及何時做出改變等信息。示例文件是:

shell$ cat mysqld-auto.cnf

{

    "Version": 1,

    "mysql_server": {

        "sort_buffer_size": {

            "Value": "32768",

            "Metadata": {

                "Timestamp": 1534230053297668,

                "User": "root",

                "Host": "localhost"

            }

        },

        "join_buffer_size": {

            "Value": "131072",

            "Metadata": {

                "Timestamp": 1534230072956789,

                "User": "root",

                "Host": "localhost"

            }

        },

        "mysql_server_static_options": {

            "slave_parallel_type": {

                "Value": "LOGICAL_CLOCK",

                "Metadata": {

                    "Timestamp": 1534230099583642,

                    "User": "root",

                    "Host": "localhost"

                }

            }

        }

    }

}

截止mariadb 10.4,該特性尚未被支持,但是已經包含在支持計劃中(https://jira.mariadb.org/browse/MDEV-16228)。

動態列

MariaDB早在5.3就引入動態列特性,主要是爲了支持一定程度上的NoSQL,只不過那會兒沒有使用JSON類型實現。該特性使得用戶能夠不更改表結構的情況下動態增加或刪除列,相比JSON類型而言,它更加的嚴格,而且尤其是在產品化系統中,這樣我們就可以只升級腳本、不強求客戶升級表結構,同時保證不會出現“找不到列“的運行時錯誤。如下所示:

mariadb> create table assets (

item_name varchar(32) primary key, -- A common attribute for all items

dynamic_cols blob -- Dynamic columns will be stored here

);

Query OK, 0 rows affected

 

mariadb> INSERT INTO assets VALUES

('MariaDB T-shirt', COLUMN_CREATE('color', 'blue', 'size', 'XL'));

 

Query OK, 1 row affected

 

mariadb> INSERT INTO assets VALUES

('Thinkpad Laptop', COLUMN_CREATE('color', 'black', 'price', 500));

 

Query OK, 1 row affected

 

mariadb> SELECT item_name, column_list(dynamic_cols) FROM assets;

 

+-----------------+---------------------------+

| item_name | column_list(dynamic_cols) |

+-----------------+---------------------------+

| MariaDB T-shirt | `size`,`color` |

| Thinkpad Laptop | `color`,`price` |

+-----------------+---------------------------+

2 rows in set

 

mariadb> SELECT item_name, COLUMN_GET(dynamic_cols, 'color' as char) AS color FROM assets;

+-----------------+-------+

| item_name | color |

+-----------------+-------+

| MariaDB T-shirt | blue |

| Thinkpad Laptop | black |

+-----------------+-------+

2 rows in set

 

mariadb> SELECT item_name,

dynamic_cols AS color FROM assets;

+-----------------+-------------------------------+

| item_name | color |

+-----------------+-------------------------------+

| MariaDB T-shirt |

| Thinkpad Laptop |

+-----------------+-------------------------------+

2 rows in set

 

mariadb> SELECT item_name, COLUMN_JSON(dynamic_cols) FROM assets;

+-----------------+-------------------------------+

| item_name | COLUMN_JSON(dynamic_cols) |

+-----------------+-------------------------------+

| MariaDB T-shirt | {"size":"XL","color":"blue"} |

| Thinkpad Laptop | {"color":"black","price":500} |

+-----------------+-------------------------------+

2 rows in set

 

mariadb> SELECT item_name, IFNULL(COLUMN_GET(dynamic_cols, 'xx' as char),'1') AS color FROM assets;

+-----------------+-------+

| item_name       | color |

+-----------------+-------+

| MariaDB T-shirt | 1     |

| Thinkpad Laptop | 1     |

+-----------------+-------+

2 rows in set

動態列配套的相關函數如下:

COLUMN_CREATE

COLUMN_ADD

COLUMN_GET

COLUMN_DELETE

COLUMN_EXISTS

COLUMN_LIST

COLUMN_CHECK

COLUMN_JSON

更多使用說明可以參考https://mariadb.com/kb/en/library/dynamic-columns/

JSON類型

在現在很多高負載的應用中,都不推薦將很大的文本例如文章的正文存儲在關係型數據庫中,相反建議存儲在NoSQL如MongoDB或ES中,但是很多應用並沒有那麼高的負載,而是僅僅爲了希望模式更加鬆散(Schemaless),因此關係型數據庫對JSON類型的支持仍然是很有價值的。

從 5.7 版本開始,MySQL 支持由 RFC 7159 定義的原生 JSON 數據類型,可以高效地訪問 JSON 文檔中的數據。

MariaDB 沒有提供這一增強功能,認爲 JSON 數據類型不是 SQL 標準的一部分。但爲了支持從 MySQL 複製數據,MariaDB 從10.2開始爲 JSON 定義了一個別名,實際上就是一個 LONGTEXT 列,如下所示。MariaDB 聲稱兩者之間沒有顯著的性能差異,但他們並沒有提供基準測試數據來支持這個說法。

CREATE TABLE t (j JSON);

DESC t;

+-------+----------+------+-----+---------+-------+

| Field | Type     | Null | Key | Default | Extra |

+-------+----------+------+-----+---------+-------+

| j     | longtext | YES  |     | NULL    |       |

+-------+----------+------+-----+---------+-------+

CREATE TABLE t2 (

  j JSON

  CHECK (JSON_VALID(j))

);

 

INSERT INTO t2 VALUES ('invalid');

ERROR 4025 (23000): CONSTRAINT `j` failed for `test`.`t2`

 

INSERT INTO t2 VALUES ('{"id": 1, "name": "Monty"}');

Query OK, 1 row affected (0.13 sec)

就MySQL/MariaDB而言,除了提供更加鬆散的模型外,還能夠用來變相實現存儲過程的可變參數支持。可能是由於MySQL/MariaDB的主要開發者本身就認爲不應該廣泛使用存儲過程的原因,可變參數這個特性社區提過多次申請,無論是mysql還是Mariadb都沒有計劃實現,這樣當存儲過程新增了一個參數後,當前的調用都需要修改,這樣就爲造成高昂的維護成本,而oracle就不存在這個問題。藉助json類型的參數,就可以變通實現可變參數的特性。

在 JSON 相關函數上,mariadb和mysql都提供了一些用於更方便地訪問、解析和檢索 JSON 數據的支持函數,雖然它們的底層實現不同,但是大部分函數都相同。爲節省篇幅,這裏就不一一列舉,讀者可以參考下面的鏈接:

https://mariadb.com/kb/en/library/json-functions/

l  https://dev.mysql.com/doc/refman/8.0/en/json-function-reference.html

不可見列

不可見列特性是mariadb 10.3引入的,其行爲是在默認的select *中不會包含不可見列,在insert table values()中插入時,也不需要爲其賦值。該特性使得在編寫了通用的查詢後,升級能夠無縫進行。如下所示:

CREATE TABLE t (x INT, y INT INVISIBLE, z INT INVISIBLE NOT NULL DEFAULT 4);

INSERT INTO t VALUES (1),(2);

INSERT INTO t (x,y) VALUES (3,33);

SELECT * FROM t;

+------+

| x    |

+------+

|    1 |

|    2 |

|    3 |

+------+

 

SELECT x,y,z FROM t;

+------+------+---+

| x    | y    | z |

+------+------+---+

|    1 | NULL | 4 |

|    2 | NULL | 4 |

|    3 |   33 | 4 |

+------+------+---+

 

DESC t;

+-------+---------+------+-----+---------+-----------+

| Field | Type    | Null | Key | Default | Extra     |

+-------+---------+------+-----+---------+-----------+

| x     | int(11) | YES  |     | NULL    |           |

| y     | int(11) | YES  |     | NULL    | INVISIBLE |

| z     | int(11) | NO   |     | 4       | INVISIBLE |

+-------+---------+------+-----+---------+-----------+

 

ALTER TABLE t MODIFY x INT INVISIBLE, MODIFY y INT, MODIFY z INT NOT NULL DEFAULT 4;

 

DESC t;

+-------+---------+------+-----+---------+-----------+

| Field | Type    | Null | Key | Default | Extra     |

+-------+---------+------+-----+---------+-----------+

| x     | int(11) | YES  |     | NULL    | INVISIBLE |

| y     | int(11) | YES  |     | NULL    |           |

| z     | int(11) | NO   |     | 4       |           |

+-------+---------+------+-----+---------+-----------+

創建視圖的話也是一樣的行爲,如下:

CREATE VIEW v1 AS SELECT * FROM t;

DESC v1;

+-------+---------+------+-----+---------+-------+

| Field | Type    | Null | Key | Default | Extra |

+-------+---------+------+-----+---------+-------+

| x     | int(11) | YES  |     | NULL    |       |

+-------+---------+------+-----+---------+-------+

 

CREATE VIEW v2 AS SELECT x,y,z FROM t;

 

DESC v2;

+-------+---------+------+-----+---------+-------+

| Field | Type    | Null | Key | Default | Extra |

+-------+---------+------+-----+---------+-------+

| x     | int(11) | YES  |     | NULL    |       |

| y     | int(11) | YES  |     | NULL    |       |

| z     | int(11) | NO   |     | 4       |       |

+-------+---------+------+-----+---------+-------+

 

mariadb> CREATE TABLE t (x INT, y INT INVISIBLE, z INT INVISIBLE NOT NULL DEFAULT 4);

 

INSERT INTO t VALUES (1),(2);

INSERT INTO t (x,y) VALUES (3,33);

 

SELECT * FROM t;

+---+

| x |

+---+

| 1 |

| 2 |

| 3 |

+---+

3 rows in set

截止到mysql 8.0,尚無支持計劃(PS:oracle 12c也支持該特性)。

虛擬列

mariadb從10.0開始支持虛擬列特性,其功能上和oracle虛擬列類似。從mariadb 10.2開始,也兼容mysql的虛擬列定義(mysql的官方子句時PERSISTENT,mariadb爲STORED)。

虛擬列索引

提到了虛擬列就不得不提索引對虛擬列的支持情況。在10.2.2以及之前版本,mariadb不支持在VIRTUAL列上創建索引,從MariaDB 10.2.3開始,同時支持VIRTUAL和PERSISTENT上創建索引。mysql 5.7則不然,除非索引列是主鍵的一部分,否則並不需要虛擬列必須爲持久化。

不可見索引

mysql 8.0支持不可見索引,mariadb不支持將index設置爲invisible,會導致優化器在選擇執行計劃時,自動忽略該索引,即便使用了FORCE INDEX。

當然,這個是由optimizer_switch變量中use_invisible_indexes選項決定的,默認爲off。如果想看一個查詢在索引調整前後執行計劃的差別,可在會話級別調整use_invisible_indexes的值。

set session optimizer_switch="use_invisible_indexes=on";

 

DROP TABLE t1;

CREATE TABLE t1(id INT PRIMARY KEY,NAME VARCHAR(10),INDEX idx_name (NAME) invisible);

SELECT table_schema,table_name,index_name,column_name,is_visible FROM information_schema.statistics WHERE is_visible='no';

ALTER TABLE t1 ALTER INDEX idx_name visible;

SELECT table_schema,table_name,index_name,column_name,is_visible FROM information_schema.statistics WHERE is_visible='no';

ALTER TABLE t1 ALTER INDEX idx_name invisible;

截止目前,尚無看到MariaDB支持該特性的計劃。

分析函數

使用分析函數,開發人員可以通過更清晰、簡潔的 SQL 代碼執行復雜分析。原來需要幾十行甚至上百行代碼完成的邏輯現在可以使用一條 SQL 語句表示複雜任務,編寫和維護速度更快、效率更高。數據庫中分析支持的處理優化可大幅提高查詢性能。以前需要自聯接或複雜過程處理的操作現在可以用原生 SQL 執行。以分組排序爲例,如果有下列表:

mysql> select * from rank_over;
+------+-------+---------------------+
| id  | subid | curd                |
+------+-------+---------------------+
|    1 |    1 | 2018-09-24 00:47:12 |
|    2 |    1 | 2018-09-24 00:47:38 |
|    3 |    1 | 2018-09-24 00:47:42 |
|    4 |    2 | 2018-09-24 00:47:50 |
|    5 |    2 | 2018-09-24 00:47:54 |
|    6 |    3 | 2018-09-24 00:48:00 |
|    7 |    4 | 2018-09-24 00:48:06 |
|    8 |    3 | 2018-09-24 01:12:10 |
|    9 |    2 | 2018-09-24 01:12:11 |
+------+-------+---------------------+

現在要取出每個subid下curd最大的1條。

使用分析函數只需要很簡單的SQL:

select t.id,t.subid,t.curd 
from(SELECT id,subid,curd,RANK() OVER(PARTITION BY subid ORDER BY curd DESC) RK
   FROM rank_over) t
where t.RK<2

如果沒有分析函數,則要複雜得多,如下:

select t1.*  from 
(select (@rowNum1:=@rowNum1+1) as rowNo,id,subid,curd from rank_over a,(Select (@rowNum1 :=0)) b order by a.subid,a.curd desc) t1 left join
(select (@rowNum2:=@rowNum2+1) as rowNo,id,subid,curd from rank_over c,(Select (@rowNum2 :=1)) d order by c.subid,c.curd desc) t2 on t1.rowNo=t2.rowNO
where t1.subid<>t2.subid or t2.subid is null

它們的結果都是:

+-------+------+-------+---------------------+
| rowNo | id  | subid | curd                |
+-------+------+-------+---------------------+
|    1 |    3 |    1 | 2018-09-24 00:47:42 |
|    4 |    9 |    2 | 2018-09-24 01:12:11 |
|    7 |    8 |    3 | 2018-09-24 01:12:10 |
|    9 |    7 |    4 | 2018-09-24 00:48:06 |
+-------+------+-------+---------------------+
4 rows in set (0.00 sec)

分析函數不僅用於提高開發效率,而且數據庫優化器通常會對分析函數的執行進行優化,典型的即是避免了對基表的二次掃描。下面梳理了mariadb和mysql分別支持的分析函數,可以發現Mariadb支持的分析函數要比mysql更加豐富。

mariadb 10.2.0引入

mysql 8.0引入,5.7不支持

CUME_DIST

CUME_DIST()

DENSE_RANK

DENSE_RANK()

FIRST_VALUE

FIRST_VALUE()

LAG

LAG()

LAST_VALUE

LAST_VALUE()

LEAD

LEAD()

MEDIAN

 

NTH_VALUE

NTH_VALUE()

NTILE

NTILE()

PERCENT_RANK

PERCENT_RANK()

PERCENTILE_CONT

 

PERCENTILE_DISC

 

RANK, ROW_NUMBER

RANK,ROW_NUMBER()

AVG

 

BIT_AND

 

BIT_OR

 

BIT_XOR

 

COUNT

 

MAX

 

MIN

 

STD

 

STDDEV

 

STDDEV_POP

 

STDDEV_SAMP

 

SUM

 

VAR_POP

 

VAR_SAMP

 

VARIANCE

 

在mysql中,分析函數的執行計劃並不體現在常規的explain輸出中,要查看關於分析函數執行計劃相關的信息,需要使用 EXPLAIN FORMAT=JSON 模式,然後查看其中windowing的部分。

遞歸CTE實現層次查詢

MariaDB從10.2.1開始支持WITH子句,10.2.2開始支持遞歸WITH。mysql則從8.0開始CTE。

CTE除了方便編寫遞歸查詢外,還有一個更重要的價值,mysql/mariadb的CTE本質上都是先創建臨時表(mysql/mariadb的cte都支持merge,mariadb還支持pushdown,mysql不支持,但是大部分情況下都會先物化),所以一般來說更加符合實際的預期,而普通的內嵌視圖有可能會被優化器選擇與主查詢合併,進而導致執行計劃欠佳(注:在mysql 8.0中,可以使用MERGE/NO_MERGE優化器提示控制子查詢是否合併到主查詢)。

很多時候我們通常僅在SELECT中使用WITH子句,實際上,WITH可以出現在很多上下文中,下面列出了WITH可以所處的上下文。

WITH ... SELECT ...

WITH ... UPDATE ...

WITH ... DELETE ...

SELECT ... WHERE id IN (WITH ... SELECT ...) ...

SELECT * FROM (WITH ... SELECT ...) AS dt ...

INSERT ... WITH ... SELECT ...

REPLACE ... WITH ... SELECT ...

CREATE TABLE ... WITH ... SELECT ...

CREATE VIEW ... WITH ... SELECT ...

DECLARE CURSOR ... WITH ... SELECT ...

EXPLAIN ... WITH ... SELECT ...

WITH [RECURSIVE] table_reference as (SELECT ...) SELECT ...

 

WITH還可以相互嵌套,如下:

WITH

engineers AS ( SELECT * FROM employees WHERE dept IN('Development','Support') ),

eu_engineers AS ( SELECT * FROM engineers WHERE country IN('NL',...) )

SELECT ... FROM eu_engineers;

 

使用CTE遞歸查詢,就可以和在oracle中一樣快速造測試數據了。

CREATE TABLE my_big_table

WITH  RECURSIVE cte

AS (SELECT 1 AS n-- anchor member

        UNION ALL

        SELECT n + 1 -- recursive member

        FROM   cte

        WHERE  n < 500000 -- terminator

       )

SELECT n,CONCAT('last-name',n) last_name FROM cte;

 

SELECT COUNT(1) FROM my_big_table;

如果是mysql 8.0,需要設置cte_max_recursion_depth參數足夠大,例如1000000,否則會報” Recursive query aborted after 1001 iterations. Try increasing @@cte_max_recursion_depth to a larger value.”

集合操作符

MariaDB從10.3開始在UNION基礎上增加了對EXCEPT和INTERSECT的支持,分別用於兩個結果集取差集和交集。例如:

CREATE TABLE seqs (i INT);

INSERT INTO seqs VALUES (1),(2),(3),(4),(5),(6);

SELECT i FROM seqs WHERE i <= 3 INTERSECT SELECT i FROM seqs WHERE i>=3;

+------+

| i    |

+------+

|    3 |

+------+

因爲mysql不支持這兩個操作,所以mysql中會報語法錯誤。

10.4開始還支持對集合操作符聲明優先級,如下:

((SELECT a FROM t1) UNION (SELECT b FROM t2)) INTERSECT (SELECT c FROM t3);

分區

都知道,在單表數據量巨大時有效採用分區能夠極大的提高SQL語句的性能(但是也需要注意的是,因爲mysql本質上實現了oracle分區本地索引的概念,所以對非唯一索引搜索性能相比非分區而言會降低,https://zhuanlan.zhihu.com/p/28703566),同時降低維護複雜性。mysql從5.1開始就引入分區功能,在隨後版本中功能增加並不多,但是在5.7.17開始採用了存儲引擎自帶的分區處理而不是作爲插件來支持。

相比其它特性來說,mariadb對分區的支持遠不如mysql,不支持hash分區,對組合分區的支持相比mysql也比較弱。下表總結了mysql和mariadb分別支持的分區類型。

mysql

mariadb

描述

HASH

不支持

 

KEY(hash的衍生版本,表達式由MySQL服務器自己決定)

不支持

 

組合分區

RANGE|LIST/HASH|KEYmysql 5.7

不支持

 

LIST

LIST

支持表達式作爲分區鍵(相當於虛擬列)

RANGE

RANGE

支持表達式作爲分區鍵(相當於虛擬列)

COLUMNS(mysql中合併稱爲COLUMNS)

RANGE COLUMNS

多列範圍分區,相當於RANGE/RANGE組合分區

LIST COLUMNS

相當於LIST/LIST組合分區

和oracle中一樣,分區在很多偏向於OLTP的SQL語句中通常是會降低性能的,這裏給出一個分區對性能影響測試的鏈接。http://www.nilinfobin.com/mysql/performance-of-inserts-on-partitions-mysql-5-6-vs-mysql-5-7/

系統版本表

系統版本表,也成爲Temporal tables表,是SQL 2011中增加的規範。他可以說是真正意義上的閃回特性。例如:

CREATE TABLE t (

   x INT

) WITH SYSTEM VERSIONING;

insert into t values(1),(2),(3);

insert into t values(4),(5),(6);

 

select now();

2018-10-23 11:58:54

select * from t;

delete from t; 

select * from t;   --此時默認查不到記錄了

SELECT * FROM t FOR SYSTEM_TIME AS OF TIMESTAMP '2018-10-23 11:58:54'

 

歷史快照查到了。和oracle一樣,還支持歷史版本查詢,如下:

SELECT * FROM t FOR SYSTEM_TIME FROM '2016-01-01 00:00:00' TO '2017-01-01 00:00:00';

在Mariadb中,其內部是基於不可見列實現的。

閃回

既然講到了系統版本表,就不得不提一下maraidb的閃回。mariadb 在10.2.4引入閃回特性,支持DML(INSERT, DELETE, UPDATE)操作的閃回,不支持DDL語句,使用閃回,必須設置binlog_row_image=FULL。

其原理和oracle有undo不一樣,將INSERT重寫爲DELETE, DELETE重寫爲INSERT, UPDATE根據前後值進行交換,這也是必須設置binlog_row_image=FULL的原因。

mysqlbinlog默認情況下會生成重做SQL,通過使用新增的"--flashback"選項,可以生成自某個SCN或者時間點以來的反向SQL。看如下對比:

mysqlbinlog /var/lib/mysql/mysql-bin.000001 -vv -d test -T mytable --start-datetime="2013-03-27 14:54:00" > review.sql

mysqlbinlog /var/lib/mysql/mysql-bin.000001 -vv -d test -T mytable --start-datetime="2013-03-27 14:54:00" --flashback > flashback.sql

執行之後,就可以通過執行mysql < flashback.sql將所有變更操作還原了。

實際上受制於mysql的體系架構,它準確的說算不上真正的閃回,隨便一個高級開發換點時間慢慢研究都可以做出來,它還不支持查詢。

除此之外,它還需要訪問到mysql的binlog,這也是個比較困難的事,因爲運維體系可能不允許用戶直接訪問mysql服務器,如果是阿里雲的RDS,就更是如此了。

對於時間點恢復這個事情,還有一種典型的做法是依賴於從庫,通過延遲複製的方式實現,這種方式用於實現OLTP或者誤操作是可以的,但是把它作爲一個撤銷操作的機制就比較強人所難了,需要人工干預的侵入性太強了。除非不得已,我們不會選擇這種實現方式,太脆弱了。

 

關於系統版本表和閃回的示例和使用模式參見:https://www.cnblogs.com/zhjh256/p/9836047.html

 

PCRE正則表達式

mariadb從10.0開始採用PCRE正則表達式,包括普通的模式匹配、替換、子串截取等,mysql雖然從5.5就已經支持正則表達式,但是在mysql 8.0之前僅僅支持最簡單的三個模式匹配,相比mariadb而言更弱。下面列出了Mysql和mariadb分別支持的正則函數和操作符。

Mysql

mariadb

NOT REGEXP

NOT REGEXP

REGEXP

REGEXP

REGEXP_INSTR()(8.0)

REGEXP_INSTR()

REGEXP_LIKE()(8.0)

REGEXP_REPLACE()(8.0)

REGEXP_REPLACE()

REGEXP_SUBSTR()(8.0)

REGEXP_SUBSTR()

RLIKE

RLIKE

注:雖然我們在應用中廣泛的使用正則表達式,但是一般不推薦在數據庫中使用正則表達式匹配和其它相關操作。

TEXT類型默認值

mariadb允許爲TEXT類型的字段設置默認值,如下:

mysql> alter table cmp1 add column id4 text default '1';

1101 - BLOB, TEXT, GEOMETRY or JSON column 'id4' can't have a default value

 

mariadb> alter table t add column id4 text default '1';

Query OK, 0 rows affected

Records: 0  Duplicates: 0  Warnings: 0

性能相關

近年來,出現了很多關於 MySQL 和 MariaDB 引擎性能的基準測試,每個廠商都會說自己比其它性能要好。所以“MySQL 或 MariaDB 哪個更快”這個問題不會有一個最終的答案,它在很大程度上取決於具體的使用場景、查詢、用戶和連接數量等因素。

不過,如果你確實想知道,下面列出了一些基準測試結果。請注意,這些測試都是在一組特定的數據庫 + 引擎+版本(例如 MySQL 5.7.4+InnoDB+10.1.3)組合上進行的,因此得出的結論只與特定的組合有關。

mysql 8.0官方的性能測試:https://www.mysql.com/why-mysql/benchmarks/

mariadb官方10.1和mysql 5.7的性能測試對比:https://mariadb.org/performance-evaluation-of-mariadb-10-1-and-mysql-5-7-4-labs-tplc/

percona server官方的percona server與mysql社區版性能測試對比:https://www.percona.com/blog/2013/10/08/a-closer-look-at-percona-server-5-6/

三方測試中,簡單的基準測試mysql普遍優於mariadb(因爲mariadb的優化器更加複雜)。

不過有一點可以確定的是,percona server無論性能還是穩定性肯定比對應的mysql社區版要表現更好。

也有測試反應在80/20、且數據較爲穩定的場景下,maraidb在啓用查詢緩存時性能優於mysql,反之mysql更優:https://www.softizy.com/blog/mariadb-10-1-mysql-5-7-performances-ibm-power-8/

         所以哪個性能更好取決於你想測試哪種場景下的表現,僅僅因爲A比B高20%而不考慮其他因素是有失偏頗的。

參數差異

mariadb和mysql在覈心參數上的差異並不大,畢竟很大一部分核心是基於mysql的,但是在很多默認值以及xtradb相關的參數上它們並不相同。mariadb官網https://mariadb.com/kb/en/library/system-variable-differences-between-mariadb-and-mysql/詳細列出了mysql和mariadb每個版本參數不一致的地方,這裏不一一列舉。這裏僅列出幾個相對比較重要的參數。

空閒事務超時時間

percona server 5.5引入了參數innodb_kill_idle_transaction用於控制空閒XtraDB事務的超時時間。因爲該特性是xtradb特性,所以從10.2開始該參數不再起作用。從mariadb 10.3開始,重新引入了幾個參數控制空閒事務的超時時間,分別是:

l  idle_transaction_timeout:控制所有事務的超時時間

l  idle_readonly_transaction_timeout:控制只讀事務的超時時間

l  idle_write_transaction_timeout:控制寫事務的超時時間

超時之後,會話會報ERROR 2006 (HY000): MySQL server has gone away,建議開發、測試環境開啓以便提前發現bug。

全局innodb日誌刷新控制

mysql參數innodb_flush_log_at_trx_commit控制 innodb日誌合適刷新到磁盤,1:每次提交時寫入文件並執行刷新;0:日誌每秒刷新一次;2:日誌每次提交時寫入,但是刷新每秒執行一次,它和0的區別在於,0只要mysql宕機就可能丟失最多1秒的事務,2則是主機宕機可能丟失1秒事務。在標準的mysql中,該參數不支持會話級修改,percona server引入了參數innodb_use_global_flush_log_at_trx_commit控制是否啓用會話級修改,當爲1時,會話可以修改innodb_flush_log_at_trx_commit的值。這在一些包含交易但是量不大的系統中很重要,它使得對不同應用採用不同的ACID嚴格程度。因爲從Mariadb 10.2開始,innodb從xtradb切換回了mysql innodb,因此XtraDB實現相關的參數不再被支持,所以該參數也無法再在mariadb中使用。

用戶統計信息

用戶統計信息會最早是mariadb 5.2(percona server也支持)引入的,由userstat參數控制,它會在INFORMATION_SCHEMA數據庫中創建CLIENT_STATISTICS/INDEX_STATISTICS/TABLE_STATISTICS/USER_STATISTICS表,分別從表、索引、客戶端、用戶層面分析使用情況,該特性使得用戶能更多的瞭解數據庫的使用模式。該特性默認不啓用,需要設置userstat = 1進行啓用。如下所示:

mariadb> show variables like '%userstat%';

+---------------+-------+

| Variable_name | Value |

+---------------+-------+

| userstat      | ON    |

+---------------+-------+

1 row in set

 

mariadb> select * from information_schema.TABLE_STATISTICS;

+--------------+------------+-----------+--------------+------------------------+

| TABLE_SCHEMA | TABLE_NAME | ROWS_READ | ROWS_CHANGED | ROWS_CHANGED_X_INDEXES |

+--------------+------------+-----------+--------------+------------------------+

| yidoo        | ebk_ebook  |      4202 |            0 |                      0 |

| mysql        | proc       |         6 |            0 |                      0 |

+--------------+------------+-----------+--------------+------------------------+

2 rows in set

 

mariadb> select * from information_schema.INDEX_STATISTICS;

+--------------+------------+------------+-----------+

| TABLE_SCHEMA | TABLE_NAME | INDEX_NAME | ROWS_READ |

+--------------+------------+------------+-----------+

| mysql        | proc       | PRIMARY    |         6 |

+--------------+------------+------------+-----------+

1 row in set

 

即時加字段INSTANT ADD COLUMN

mysql 8.0和mariadb 10.3均支持即時加字段,該特性的實現原理是對於新增的字段,它對於爲大表新增字段特別有價值,原來爲一張數千萬的表新增字段可能需要10多分鐘,現在瞬間就可以完成,如下所示:

-- mariadb 10.3

alter table cmp1 add column id2 text default '1';

受影響的行: 0

時間: 0.004s

 

update cmp1 set id2 = i;

受影響的行: 1048576

時間: 18.394s

 

alter table cmp1 drop column id2;

受影響的行: 0

時間: 2.348s

 

-- mariadb 10.2

CREATE TABLE `cmp1` (

`i` varchar(128) /*!100301 COMPRESSED*/ COLLATE gbk_bin DEFAULT NULL

) ENGINE=InnoDB DEFAULT CHARSET=gbk COLLATE=gbk_bin;

 

insert into cmp1 values('3isfuues');

insert into cmp1 select * from cmp1; -- 插入100萬行

alter table cmp1 add column id2 text;

受影響的行: 0

時間: 2.911s

 

update cmp1 set id2 = i;

受影響的行: 1048576

時間: 51.435s

 

alter table cmp1 drop column id2;

受影響的行: 0

時間: 3.530s

 

查詢:alter table cmp1 add column id2 text -- mysql 5.7

共 0 行受到影響

執行耗時 : 6.317 sec

傳送時間 : 1.765 sec

總耗時 : 8.083 sec

alter table cmp1 drop column id2

共 0 行受到影響

執行耗時 : 4.764 sec

傳送時間 : 1.764 sec

總耗時 : 6.529 sec

在這一點上,mariadb/mysql比oracle做得更好,oracle對於新增包含默認值的字段,仍然是採用即時更新的實現方式。

表連接方式

雖然mariadb從5.3版本開始在表連接之間增加了哈希連接,但是mariadb的哈希連接實現並沒有從頭開始實現,而是和嵌套循環連接使用了相同的前半部分,即所有的處理都是基於塊循環,而不像oracle只要PGA足夠就一次性將整個驅動表創建爲哈希表,然後循環一遍關聯表即可。這種實現方式的結果就是性能提升比較難以做到數量級提升。

create table big_table_a as select * from columns; -- 插入506240行

create table big_table_b as select * from columns; -- 插入506240行

create index idx_big_table_a on big_table_a(table_name,column_name);

create index idx_big_table_b on big_table_b(table_name,column_name);

mariadb> desc big_table_a;

+--------------------------+---------------------+------+-----+---------+-------+

| Field                    | Type                | Null | Key | Default | Extra |

+--------------------------+---------------------+------+-----+---------+-------+

| TABLE_CATALOG            | varchar(512)        | NO   |     |         |       |

| TABLE_SCHEMA             | varchar(64)         | NO   |     |         |       |

| TABLE_NAME               | varchar(64)         | NO   | MUL |         |       |

| COLUMN_NAME              | varchar(64)         | NO   |     |         |       |

| ORDINAL_POSITION         | bigint(21) unsigned | NO   |     | 0       |       |

| COLUMN_DEFAULT           | longtext            | YES  |     | NULL    |       |

| IS_NULLABLE              | varchar(3)          | NO   |     |         |       |

| DATA_TYPE                | varchar(64)         | NO   |     |         |       |

| CHARACTER_MAXIMUM_LENGTH | bigint(21) unsigned | YES  |     | NULL    |       |

| CHARACTER_OCTET_LENGTH   | bigint(21) unsigned | YES  |     | NULL    |       |

| NUMERIC_PRECISION        | bigint(21) unsigned | YES  |     | NULL    |       |

| NUMERIC_SCALE            | bigint(21) unsigned | YES  |     | NULL    |       |

| DATETIME_PRECISION       | bigint(21) unsigned | YES  |     | NULL    |       |

| CHARACTER_SET_NAME       | varchar(32)         | YES  |     | NULL    |       |

| COLLATION_NAME           | varchar(32)         | YES  |     | NULL    |       |

| COLUMN_TYPE              | longtext            | NO   |     | NULL    |       |

| COLUMN_KEY               | varchar(3)          | NO   |     |         |       |

| EXTRA                    | varchar(30)         | NO   |     |         |       |

| PRIVILEGES               | varchar(80)         | NO   |     |         |       |

| COLUMN_COMMENT           | varchar(1024)       | NO   |     |         |       |

| IS_GENERATED             | varchar(6)          | NO   |     |         |       |

| GENERATION_EXPRESSION    | longtext            | YES  |     | NULL    |       |

+--------------------------+---------------------+------+-----+---------+-------+

22 rows in set

 

show variables like '%join%'; -- 控制連接相關算法是否可用以及工作區緩存大小

set join_cache_level=8;

set join_buffer_size = 1048576000;

set join_buffer_space_limit = 1048576000;

set query_cache_type = off; -- 驗證每次重新執行,而不是從緩存獲取,否則瞬間就查出來了

 

select count(1) from big_table_a a ,big_table_b b

where a.column_name = b.column_name

and a.table_name = b.table_name;

-- hj a,b均索引,平均10.5秒,索引字段數和表字段數相差很多,且表大部分字段都有值,但是索引覆蓋掃描效果卻一點都不明顯

  684519403

- 685679789

    116w  Innodb_buffer_pool_read_requests

-- nl,a\b均索引,平均27秒

685682832

734794328

 4911w   Innodb_buffer_pool_read_requests

-- hj a全表,b索引,平均11秒

734931616

735957796

  102w   Innodb_buffer_pool_read_requests

慢日誌差別

mariadb和percona server分別在mysql慢日誌的基礎上增加了額外的統計信息,包括精確的執行時間、innodb相關的統計信息等。

log_slow_verbosity

該參數爲percona server/mariadb的擴展。用於控制慢日誌統計信息記錄的詳細程度,mariadb 取值範圍包括query_plan, innodb, explain。

percona server取值範圍包括:

l  microtime: 記錄查詢統計信息到微妙精度。

l  query_plan: 記錄查詢執行計劃。

l  innodb: 記錄InnoDB的統計信息。

l  minimal: 相當於僅開啓microtime。

l  standard: 相當於開啓microtime,innodb。

l  full: 啓用除了profiling和profiling_use_getrusage之外的選項。

l  profiling(一般不建議開啓): 啓用所有連接的所有profiling。

l  profiling_use_getrusage: 啓用profiling。

參見:

https://mariadb.com/kb/en/library/server-system-variables/#log_slow_verbosity

https://www.percona.com/doc/percona-server/LATEST/diagnostics/slow_extended.html

log_slow_filter

該參數用於控制記錄慢日誌的過濾條件,percona server的取值範圍爲:

l  full_scan: 查詢通過全表掃描執行。

l  full_join: 查詢關聯時沒有走索引。

l  tmp_table: 查詢創建了內部臨時表。

l  tmp_table_on_disk: 查詢創建了磁盤臨時表。

l  filesort: 查詢執行了filesort。

l  filesort_on_disk: 查詢執行了基於磁盤的filesort。

mariab除了上述取值範圍外,還包括下列幾個取值:

l  query_cache:語句直接通過查詢緩存返回結果。

l  query_cache_miss:語句沒有使用查詢緩存返回。

l  admin:非普通增刪改查語句如create, optimize, drop等。

慢日誌對於服務器性能是有一定影響的,具體影響程度參見https://www.percona.com/blog/2009/02/10/impact-of-logging-on-mysql%E2%80%99s-performance/

慢日誌分析工具

mysqldumpslow

mysqldumpslow是mysql自帶的用於從慢日誌中分析彙總語句的工具。

pt-query-digest

它是percona toolkit包中包含的一個可以從慢日誌、processlist以及tcpdump分析查詢的工具。相比mysqldumpslow而言,它提供了更多的信息幫助時候分析性能。具體可參見https://www.percona.com/doc/percona-toolkit/LATEST/pt-query-digest.html

優化器提示/跟蹤

準確的說,mysql從5.7開始真正算引入了優化器提示,其用法和oracle的優化器提示完全相同,5.6以及之前版本的索引提示等雖然能夠幫助優化,但是其價值遠沒有5.7大。mysql 8.0對此進行了進一步加強,引入了更多的優化器提示。如下所示:

mariadb支持的優化器提示(mysql均支持)

mysql 5.7開始新增的優化器提示

SQL_CACHE / SQL_NO_CACHE

 

BKANO_BKA

SQL_BUFFER_RESULT 

BNLNO_BNL

SQL_SMALL_RESULT / SQL_BIG_RESULT

 

INDEX_MERGENO_INDEX_MERGE

STRAIGHT_JOIN 

JOIN_FIXED_ORDER

SQL_CALC_FOUND_ROWS

該提示用在分頁查詢上,帶了該提示後,MariaDB會計算不帶LIMIT的時候,有多少行記錄。隨後可通過FOUND_ROWS()獲取行數

JOIN_ORDER

USE/FORCE/IGNORE INDEX

JOIN_PREFIX

 

JOIN_SUFFIX

 

MAX_EXECUTION_TIME

 

MERGENO_MERGE

 

MRRNO_MRR

 

NO_ICP

 

NO_RANGE_OPTIMIZATION

 

QB_NAME

 

RESOURCE_GROUP

 

SEMIJOINNO_SEMIJOIN

 

SKIP_SCANNO_SKIP_SCAN

 

SET_VAR

 

SUBQUERY

從上可知,相比Mysql而言,Mariadb的提示幾乎少得可憐。各優化器提示的含義參見https://dev.mysql.com/doc/refman/5.7/en/optimizer-hints.html

對於MySQL,用戶可以用EXPLAIN檢查優化器提示是如何影響執行計劃的,如果要查看某個優化器提示是否被使用了,可以在執行EXPLAIN後執行 SHOW WARNINGS,EXPLAIN EXTENDED可以看到哪個提示被用了,如下:

 

 

 

Mariadb則沒有該特性。

查看正在運行SQL的執行計劃

mysql和mariadb都提供了查看正在執行的sql的執行計劃。其中:

l  MariaDB可以通過執行SHOW EXPLAIN FOR <thread_id>查看。除了根據線程號、查詢號終止外,還支持中止某個用戶的所有連接。如下:

KILL [HARD | SOFT] [CONNECTION | QUERY [ID] ] [thread_id | USER user_name | query_id]

默認爲殺連接。

l  MySQL可以通過EXPLAIN FOR CONNECTION <thread_id>查看,但是mysql沒有提供用戶級別的。如下所示:

KILL [CONNECTION | QUERY] processlist_id

 

性能視圖

mysql從5.5版本引入PERFORMANCE_SCHEMA,並且在隨後的版本中得到增強。MariaDB 10.2集成了MySQL 5.6 PERFORMANCE_SCHEMA,但是隨後一直沒有加強,截止mariadb 10.4仍然是最早mysql 5.6版本的P_S。

mysql從5.7.7開始在performance_schema的基礎上新增了sys性能庫,它是基於performance_schema的視圖,還包括一些存儲過程,在此之前, 通常需要自己編寫自定義sql查詢performance_schema,其友好性相對來說不是很好。在mysql 8.0中,該庫仍然存在並且進行了加強,https://dev.mysql.com/doc/refman/8.0/en/sys-schema.html。mariadb標準安裝包並不包含sys庫,但是用戶可以通過執行5.6版本的腳本https://github.com/mysql/mysql-sys自行安裝。

analyze

explain只能查看SQL語句的解釋計劃,它並不總是和實際的執行計劃相同,爲此,mariadb10.1增加了analyze語句,包含了要查看實際的執行計劃,它會先執行SQL語句,然後顯示執行計劃以及相關的資源消耗統計。如下所示:

ANALYZE SELECT *

FROM orders, customer

WHERE

  customer.c_custkey=orders.o_custkey AND

  customer.c_acctbal < 0 AND

  orders.o_totalprice > 200*1000

+----+-------------+----------+------+---------------+-------------+---------+--------------------+--------+--------+----------+------------+-------------+

| id | select_type | table    | type | possible_keys | key         | key_len | ref                | rows   | r_rows | filtered | r_filtered | Extra       |

+----+-------------+----------+------+---------------+-------------+---------+--------------------+--------+--------+----------+------------+-------------+

|  1 | SIMPLE      | customer | ALL  | PRIMARY,...   | NULL        | NULL    | NULL               | 149095 | 150000 |    18.08 |       9.13 | Using where |

|  1 | SIMPLE      | orders   | ref  | i_o_custkey   | i_o_custkey | 5       | customer.c_custkey |      7 |     10 |   100.00 |      30.03 | Using where |

+----+-------------+----------+------+---------------+-------------+---------+--------------------+--------+--------+----------+------------+-------------+

其中r_開頭的代表實際的結果,不帶r_的爲優化器基於統計信息估計的結果。注:順帶提一下,如果過濾後符合(filtered)條件的記錄比例高於15%,一般來說就說明索引不合理。有時候會看到r_rows或r_filtered爲NULL,這說明該對象沒有被實際掃描或沒有符合條件的記錄。

截止目前位置,mysql尚不支持該特性,也無增加的計劃。

進度報告

mariadb從5.3版本開始在INFORMATION_SCHEMA.PROCESSLIST 中增加了一個PROGRESS 列,其中包含一些很可能較慢的操作的進度信息,類似於oracle的v$session_longops視圖。當前報告進度的操作包括:

ALTER TABLE

CREATE INDEX

DROP INDEX

LOAD DATA INFILE(LOAD DATA LOCAL INFILE暫不支持,因爲文件在客戶端,所以無法預知大小)

因爲目前不支持查看SQL語句的進度,該特性目前幫助不是特別大。

參見:https://mariadb.com/kb/en/library/progress-reporting/

 

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