MySQL與MariaDB主要特性比較詳細版v1.0(不含HA).pdfhtml
連接:https://pan.baidu.com/s/1qAcrxg8eRumRi3FTJtXZxw
提取碼:giei mysql
MySQL與MariaDB主要特性核心正式版v1.0.pdfgit
連接:https://pan.baidu.com/s/1yAKU7GIV4LDQRCvnx7oEnQ
提取碼:1d6t github
若是你但願編寫的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年筆者在公司論壇的帖子也是這麼說的。因此存在即合理,只是應該根據實際狀況選擇儘量最知足要求的那個。sql
限於篇幅,本文只介紹筆者認爲比較重要的那些特性,對於可能有差異可是我的認爲並不會有不少成本差別的特性或並不太合適在數據庫中實現的特性,文本並不作討論;對於一些對開發或性能並無那麼重要的特性例如MySQL Shell、密鑰管理、默認身份認證等亦如是;第三,文本也不會討論MySQL和MariaDB在實現底層細節上的差別,例如對於子查詢中的order by,mysql會照實進行排序,可是mariadb會擇機優化去掉排序,它確實對於分析性能有幫助,但不是本文核心。在版本上,筆者主要參考MySQL5.7/8.0和MariaDB 10.3爲主,必要的時候也會說起以前的版本。mongodb
咱們先來看一下MySQL的分支發展史,以下圖所示:shell
從上可知,總體而言,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方式暫不被支持。
注:原本筆者的初衷是以計劃以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存儲引擎,它能夠爲大多數工做負載提供相似的優點,而且能夠更好地優化對現代硬件的支持。
mysql 5.6和mariadb 10.0引入了GTID(全局事務ID)特性,它的目的在於使用Gtid的Mysql可以在整個複製環境中可以自動的切換,而不像之前須要指定文件和位置。可是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的線程池。
在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;
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.二、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;
在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)」。
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
默認狀況下,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/
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
動態列配套的相關函數以下:
更多使用說明能夠參考https://mariadb.com/kb/en/library/dynamic-columns/。
在如今不少高負載的應用中,都不推薦將很大的文本例如文章的正文存儲在關係型數據庫中,相反建議存儲在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 數據的支持函數,雖然它們的底層實現不一樣,可是大部分函數都相同。爲節省篇幅,這裏就不一一列舉,讀者能夠參考下面的連接:
l 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不支持 |
|
|
|
|
RANK, ROW_NUMBER |
|
在mysql中,分析函數的執行計劃並不體如今常規的explain輸出中,要查看關於分析函數執行計劃相關的信息,須要使用 EXPLAIN FORMAT=JSON 模式,而後查看其中windowing的部分。
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服務器本身決定) |
不支持 |
|
組合分區
|
不支持 |
|
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或者誤操做是能夠的,可是把它做爲一個撤銷操做的機制就比較強人所難了,須要人工干預的侵入性太強了。除非不得已,咱們不會選擇這種實現方式,太脆弱了。
關於系統版本表和閃回的示例和使用模式參見:http://www.javashuo.com/article/p-hqnglrpy-bq.html
mariadb從10.0開始採用PCRE正則表達式,包括普通的模式匹配、替換、子串截取等,mysql雖然從5.5就已經支持正則表達式,可是在mysql 8.0以前僅僅支持最簡單的三個模式匹配,相比mariadb而言更弱。下面列出了Mysql和mariadb分別支持的正則函數和操做符。
Mysql |
mariadb |
|
|
|
無 |
|
|
|
|
RLIKE |
RLIKE |
注:雖然咱們在應用中普遍的使用正則表達式,可是通常不推薦在數據庫中使用正則表達式匹配和其它相關操做。
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。
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
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相關的統計信息等。
該參數爲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
該參數用於控制記錄慢日誌的過濾條件,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是mysql自帶的用於從慢日誌中分析彙總語句的工具。
它是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
|
|
|
|
SQL_SMALL_RESULT / SQL_BIG_RESULT
|
|
|
|
SQL_CALC_FOUND_ROWS 該提示用在分頁查詢上,帶了該提示後,MariaDB會計算不帶LIMIT的時候,有多少行記錄。隨後可經過FOUND_ROWS()獲取行數 |
|
USE/FORCE/IGNORE INDEX |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
從上可知,相比Mysql而言,Mariadb的提示幾乎少得可憐。各優化器提示的含義參見https://dev.mysql.com/doc/refman/5.7/en/optimizer-hints.html。
對於MySQL,用戶能夠用EXPLAIN檢查優化器提示是如何影響執行計劃的,若是要查看某個優化器提示是否被使用了,能夠在執行EXPLAIN後執行 SHOW WARNINGS,EXPLAIN EXTENDED能夠看到哪一個提示被用了,以下:
Mariadb則沒有該特性。
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自行安裝。
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視圖。當前報告進度的操做包括:
l LOAD DATA INFILE(LOAD DATA LOCAL INFILE暫不支持,由於文件在客戶端,因此沒法預知大小)
由於目前不支持查看SQL語句的進度,該特性目前幫助不是特別大。