一.MySQL8.0簡介
mysql8.0現在已經發布,2016-09-12第一個DM(development milestone)版本8.0.0發布。新的版本帶來很多新功能和新特性,對性能也得到了很大對提升。官方表示 MySQL 8 比之前mysql版本有很大提升,它的速度是 MySQL 5.7 2 倍,如下圖對比所示
mysql8.0官方文檔: https://dev.mysql.com/doc/refman/8.0/en/
二.MySQL8.0新增的特性
mysql8.0新增的特性主要有以下幾個方面:
1.賬戶與安全
1)用戶創建和授權是分開的,並修改了默認的認證插件。
2)增加了密碼重用策略,支持修改密碼時要求用戶輸入當前密碼。
3)支持角色功能。
提高了用戶和密碼管理的安全性,方便了權限的管理。
2.優化器索引
三種新的索引方式
1)支持隱藏索引,方便索引的維護和性能調試。
2)支持降序索引,提高了特定場景的查詢性能。
3)支持函數索引,擴展了索引支持的數據類型,可以對更多的數據類型進行索引。
3.通用表表達式(Common Table Expressions:CTE)
1)非遞歸CTE,提高查詢的性能和代碼的可讀性。
2)遞歸CTE,支持通過對數據遍歷和遞歸的實現完成SQL實現強大復雜的功能。
4.窗口函數(Window Functions)
是一種新的查詢方式。窗口函數有兩類,一類上傳統的聚合函數作為窗口函數使用,另一類是專用的窗口函數。可以實現復雜的數據分析能力。
5.InnoDB存儲引擎增強
1)新的數據字典可以對元數據統一的管理,同時也提高了更好的查詢性能和可靠性。
2)原子DDL的操作,提供了更加可靠的管理。
3)自增列的持久化,解決了長久以來自增列重復值的bug。
4)死鎖檢查控制,可以選擇在高並發的場景中關閉,提高對高並發場景的性能。
5)鎖定語句選項,可以根據不同業務需求來選擇鎖定語句級別。
6.JSON增強
新的運算符及JSON相關函數。
mysql8.0新特性更多可以查看這篇:https://mysqlserverteam.com/whats-new-in-mysql-8-0-generally-available/
二.本機操作環境
先看下本機mysql環境和狀態
mysql> show variables like '%%version%';
+--------------------------+------------------------------+ | Variable_name | Value | +--------------------------+------------------------------+ | immediate_server_version | 999999 | | innodb_version | 8.0.16 | | original_server_version | 999999 | | protocol_version | 10 | | slave_type_conversions | | | tls_version | TLSv1,TLSv1.1,TLSv1.2 | | version | 8.0.16 | | version_comment | MySQL Community Server - GPL | | version_compile_machine | x86_64 | | version_compile_os | Linux | | version_compile_zlib | 1.2.11 | +--------------------------+------------------------------+
查看狀態
mysql80>status -------------- mysql Ver 14.14 Distrib 5.7.26, for Linux (x86_64) using EditLine wrapper Connection id: 2 Current database: Current user: root@localhost SSL: Not in use Current pager: stdout Using outfile: '' Using delimiter: ; Server version: 5.7.26 MySQL Community Server (GPL) Protocol version: 10 Connection: Localhost via UNIX socket Server characterset: latin1 Db characterset: latin1 Client characterset: latin1 Conn. characterset: latin1 UNIX socket: /var/run/mysqld/mysqld.sock Uptime: 27 min 38 sec
三.MySQL8.0新特性
賬戶與安全變更:增加新的安全策略,增加角色功能。
1.創建用戶和用戶授權的命令是分開執行。
先看下mysql5.7是如何創建用戶和授權的。
mysql5.7中查詢默認用戶,以下是mysql5.7中的默認的三個用戶。
mysql5.7> select user,host from mysql.user; +---------------+-----------+ | user | host | +---------------+-----------+ | mysql.session | localhost | | mysql.sys | localhost | | root | localhost | +---------------+-----------+
mysq5.7grant可以完成用戶創建和授權同時操作。
mysql> grant all privileges on *.* to 'song'@'%' identified by 'song@2019'; #創建新的用戶並賦予在所有主機登陸及密碼 mysql> select user,host from mysql.user; +---------------+-----------+ | user | host | +---------------+-----------+ | root | % | | song | % | | mysql.session | localhost | | mysql.sys | localhost | | root | localhost | +---------------+-----------+
mysql8.0創建用戶和授權命令如下,分兩步進行。
create user 'song'@'%' identified by 'Song@2019'; //創建用戶 grant all privileges on *.* to 'song'@'%'; //授權
mysql8.0
mysql> select host,user from mysql.user; +-----------+------------------+ | host | user | +-----------+------------------+ | localhost | mysql.infoschema | | localhost | mysql.session | | localhost | mysql.sys | | localhost | root | +-----------+------------------+
創建用戶
mysql> create user 'song'@'%' identified by 'song@2019'; mysql> select host,user from mysql.user; +-----------+------------------+ | host | user | +-----------+------------------+ | % | song | | localhost | mysql.infoschema | | localhost | mysql.session | | localhost | mysql.sys | | localhost | root |
授權
mysql> grant all privileges on *.* to 'song'@'%';
這樣分兩次的好處是語句的語義更加清晰點。
2.認證的插件更新
mysql8.0中默認的身份認證插件是caching_sha2_password,替代了之前的mysql_navtive_password,這個新的認證會更加安全。
通過系統變量查詢,下面這是mysql5.7的,發現mysql5.7還是使用mysql_native_password這個插件。
mysql> show variables like 'default_authentication%'; +-------------------------------+-----------------------+ | Variable_name | Value | +-------------------------------+-----------------------+ | default_authentication_plugin | mysql_native_password | +-------------------------------+-----------------------+
看下mysq8.0
mysql> show variables like 'default_authentication%'; +-------------------------------+-----------------------+ | Variable_name | Value | +-------------------------------+-----------------------+ | default_authentication_plugin | caching_sha2_password | +-------------------------------+-----------------------+
也可以通過用戶表user查看plugin這一列。
mysql> select user,host,plugin from mysql.user; +------------------+-----------+-----------------------+ | user | host | plugin | +------------------+-----------+-----------------------+ | root | % | caching_sha2_password | | mysql.infoschema | localhost | caching_sha2_password | | mysql.session | localhost | caching_sha2_password | | mysql.sys | localhost | caching_sha2_password | | root | localhost | caching_sha2_password | +------------------+-----------+-----------------------+
不過如果連接客戶端比較老舊沒有升級,在連接mysql8.0時候可能會認證錯誤。如果想要使用之前老的認證方式可以在配置文件里將default-authentication-plugin這段開啟,然后重啟數據庫。
如果想對某個用戶使用老的認證方式,可以使用下面語句
alter user 'song'@'%' identified with mysql_native_password by 'Songpasswd';
with后面可以跟新的認證插件也可以是老的認證插件。
3.密碼管理策略增強
mysql8.0開始允許限制重復使用以前的密碼。也就是說在修改密碼的時候不能改為以前使用過的密碼。
mysql8.0中有幾個系統變量來實現控制密碼修改策略。
1)password_history:該參數數值用於設置歷史密碼可以再次使用之前需要進行密碼修改的次數。比如password_history = 3 就是代表新密碼不能和最近使用過3次的密碼相同。設置為0則不會對歷史密碼是否可以重用進行限制。
2)password_reuse_interval:按照日期指定來限制,比如 password_reuse_interval = 60 表示新密碼不能和60天之內的密碼相同,默認值為0,設置為0則不會對歷史密碼重用進行時間間隔設置。
3)password_require_current:默認值是OFF,當值為ON時候用戶修改密碼時候是否需要提供當前密碼。
查看變量,都是以password開頭的。
mysql> show variables like 'password%'; +--------------------------+-------+ | Variable_name | Value | +--------------------------+-------+ | password_history | 0 | | password_require_current | OFF | | password_reuse_interval | 0 | +--------------------------+-------+
默認情況下password_history和password_reuse_interval的值是0,表示不做限制,而password_require_current的值是off,也就是說默認情況下這些值都是沒有啟用的。
設置方式,在之前mysql版本中如果給運行中mysql修改參數只能在當前mysql進程中設置,但是mysql重啟后就會失效,二如果寫入配置文件my.cnf里則需要重啟服務,這兩種方法都不太方便,而在mysql8.0增加了新特性就是在線修改系統變量,並將修改后的持久化到磁盤,重啟服務依然有效。它在mysql目錄下增加了一個配置文件mysqld-auto.cnf。
mysql> set persist password_history=6; mysql> show variables like 'password%'; +--------------------------+-------+ | Variable_name | Value | +--------------------------+-------+ | password_history | 6 | | password_require_current | OFF | | password_reuse_interval | 0 | +--------------------------+-------+
查看mysqld-auto.cnf,這是一個json格式的配置文件。mysql服務器在重啟的時候就會讀取這個文件。
root@f488b1c2586a:/# cat /var/lib/mysql/mysqld-auto.cnf { "Version" : 1 , "mysql_server" : { "password_history" : { "Value" : "6" , "Metadata" : { "Timestamp" : 1563796829915492 , "User" : "root" , "Host" : "localhost" } } } }
對於的修改也可以針對用戶去修改。
mysql> select user,host,Password_reuse_history from mysql.user; #先查看 +------------------+-----------+------------------------+ | user | host | Password_reuse_history | +------------------+-----------+------------------------+ | root | % | NULL | | song | % | NULL | | mysql.infoschema | localhost | NULL | | mysql.session | localhost | NULL | | mysql.sys | localhost | NULL | | root | localhost | NULL | +------------------+-----------+------------------------+ mysql> alter user 'song'@'%' password history 10; #設置10 mysql> select user,host,Password_reuse_history from mysql.user; #查看設置成功 +------------------+-----------+------------------------+ | user | host | Password_reuse_history | +------------------+-----------+------------------------+ | root | % | NULL | | song | % | 10 | | mysql.infoschema | localhost | NULL | | mysql.session | localhost | NULL | | mysql.sys | localhost | NULL | | root | localhost | NULL | +------------------+-----------+------------------------+
設置成功可以測試下,發現密碼不可修改並報錯。
mysql> alter user 'song'@'%' identified by 'song_passwd'; ERROR 3689 (HY000): Cannot use these credentials for 'song'@'%' because they contradict the password history policy
4.角色管理
mysql8.0提供了角色管理新功能,角色是一組權限的集合。
在之前的mysql版本中,要給某些用戶分配權限需要一個個分配。如果用戶比較多,角色也比較多,手動分配管理起來就比較麻煩。
新的版本有了角色功能之后,在權限和用戶之間加了一層角色。可以將一組定義好的權限賦予某個角色,可以在將角色分配給需要的用戶。這樣可以簡化用戶權限的管理。
步驟:
1.先創建角色
2.給這個角色賦予事先定義好的權限。
3.給角色授權給某個用戶。
操作流程:
操作步驟 | 說明 |
mysql> create database roleDB ; | 創建一個數據庫 |
create table roleDB.table_auth(id int); | 創建一張表 |
create role 'write_role'; | 創建一個角色 |
mysql> select host,user,authentication_string from mysql.user; +-----------+------------------+------------------------------------------------------------------------+ | host | user | authentication_string | +-----------+------------------+------------------------------------------------------------------------+ | % | root | $A$005$OI6M7iPSa8RTaSQ4NXvRjLWQ2Qf3JUMlS1NrQTPdvhEUh/bfIIdBj. | | % | song | $A$005$GAy/g`mMyQa.gojqBMnFSqaTpD6DZFZPExMVjFmxVDU45RkiAvsH4qFb9Y9 | | % | write_role | | | localhost | mysql.infoschema | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | localhost | mysql.session | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | localhost | mysql.sys | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | localhost | root | $A$005$rX oqug@ ~HH%H XLsY.hAnb2p8G5JXazsp/2qDxaSLEZpDVVfRnSKrre6 | +-----------+------------------+------------------------------------------------------------------------+ |
查看用戶信息表, 這里write_role是一個沒有密碼的用戶, 可以看出mysql里的角色其實是一個用戶。 是用用戶來模擬角色的效果。 |
mysql> grant select,insert,update,delete on roleDB.* to 'write_role'; Query OK, 0 rows affected (0.01 sec) |
授權,授予roleDB庫上的增刪改查權限, 這里對角色和用戶的授權語法都是 一樣的。 |
mysql> create user 'yonhu_role1' identified by 'password123'; Query OK, 0 rows affected (0.01 sec) |
創建用戶並賦予密碼 |
mysql> grant 'write_role' to 'yonhu_role1'; Query OK, 0 rows affected (0.00 sec) |
將角色授予用戶 |
mysql> show grants for 'yonhu_role1'; +---------------------------------------------+ | Grants for yonhu_role1@% | +---------------------------------------------+ | GRANT USAGE ON *.* TO `yonhu_role1`@`%` | | GRANT `write_role`@`%` TO `yonhu_role1`@`%` | +---------------------------------------------+ |
顯示用戶的權限 第一條是默認使用權限 第二條就是剛剛賦予的角色 |
mysql> show grants for 'yonhu_role1' using 'write_role'; +-----------------------------------------------------------------+ | Grants for yonhu_role1@% | +-----------------------------------------------------------------+ | GRANT USAGE ON *.* TO `yonhu_role1`@`%` | | GRANT SELECT,INSERT, UPDATE, DELETE ON `roleDB`.* TO `yonhu_role1`@`%` | | GRANT `write_role`@`%` TO `yonhu_role1`@`%` | +-----------------------------------------------------------------+ |
具體查看用戶角色擁有的權限 |
exit | 退出 |
root@f488b1c2586a:/# mysql -uyonhu_role1 -p mysql> select user(); |
用yonhu_role1用戶登陸 |
|
實際table_auth表上存在的,也 擁有權限去查詢。 |
mysql> select current_role(); |
查看當前角色,默認沒有激活 這就是沒法查詢原因。
|
mysql> set role 'write_role'; mysql> select * from roleDB.table_auth; |
設置角色,再次查詢就可以 |
root@f488b1c2586a:/# mysql -uroot -p mysql> set default role 'write_role' to 'yonhu_role1'; |
為每個用戶設置默認角色 |
mysql> select * from mysql.default_roles; mysql> select * from mysql.role_edges; |
查看用戶角色信息,這個是mysql8 新增加的表。 下面是用戶鎖設計到角色的信息。 |
也可以撤銷角色
mysql> revoke insert,update,delete on roleDB.* from 'write_role'; mysql> show grants for 'write_role'; +------------------------------------------------+ | Grants for write_role@% | +------------------------------------------------+ | GRANT USAGE ON *.* TO `write_role`@`%` | | GRANT SELECT ON `roleDB`.* TO `write_role`@`%` | +------------------------------------------------+ 2 rows in set (0.00 sec) mysql> show grants for yonhu_role1; +---------------------------------------------+ | Grants for yonhu_role1@% | +---------------------------------------------+ | GRANT USAGE ON *.* TO `yonhu_role1`@`%` | | GRANT `write_role`@`%` TO `yonhu_role1`@`%` |
優化器索引
mysq8.0增加了三種新的索引方式,降序索引,影藏索引,函數索引。
1.隱藏索引(invisiable index):也叫做不可見索引,它不會被優化器使用,也就是對優化器不可見,但是但仍然需要維護。
應用場景:
1)軟刪除:可以先隱藏索引,查詢優化器不會使用該索引,但是索引還是在維護,當最終確認刪除后系統不受影響,就可以徹底刪除索引)。
2)灰度發布:測試一些索引功能,在線上測試,查詢不會影響,確認索引有效,某些功能受能用到的,在將索引可見)。
3)新索引替換老索引。
mysql> create table user(i int,j int); mysql> create index i_index on user(i); mysql> create index j_index on user(j) invisible;
顯示索引信息
mysql> show index from user\G; *************************** 1. row *************************** Table: user Non_unique: 1 Key_name: i_index Seq_in_index: 1 Column_name: i Collation: A Cardinality: 0 Sub_part: NULL Packed: NULL Null: YES Index_type: BTREE Comment: Index_comment: Visible: YES Expression: NULL *************************** 2. row *************************** Table: user Non_unique: 1 Key_name: j_index Seq_in_index: 1 Column_name: j Collation: A Cardinality: 0 Sub_part: NULL Packed: NULL Null: YES Index_type: BTREE Comment: Index_comment: Visible: NO Expression: NULL 2 rows in set (0.06 sec)
查看執行計划任務
mysql> explain select * from user where i=1; +----+-------------+-------+------------+------+---------------+------------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------------+---------+-------+------+----------+-------+ | 1 | SIMPLE | user | NULL | ref | user_index | i_index | 5 | const | 1 | 100.00 | NULL | +----+-------------+-------+------------+------+---------------+------------+---------+-------+------+----------+-------+ mysql> explain select * from user where j=1; +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+ | 1 | SIMPLE | user | NULL | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | Using where | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
查看查詢優化器開關
mysql> select @@optimizer_switch\G; *************************** 1. row *************************** @@optimizer_switch: index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,
engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,block_nested_loop=on,batched_key_access=off,
materialization=on,semijoin=on,loosescan=on,firstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on,
use_index_extensions=on,condition_fanout_filter=on,derived_merge=on,use_invisible_indexes=off,skip_scan=on 1 row in set (0.00 sec)
在會話級別打開它
mysql> set session optimizer_switch="use_invisible_indexes=on"; mysql> select @@optimizer_switch\G; *************************** 1. row *************************** @@optimizer_switch: index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,
engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,block_nested_loop=on,batched_key_access=off,
materialization=on,semijoin=on,loosescan=on,firstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on,
use_index_extensions=on,condition_fanout_filter=on,derived_merge=on,use_invisible_indexes=on,skip_scan=on
設置成功,查詢發現已經打開.
在此查看,可以使用不可見索引
mysql> explain select * from user where j=1; +----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------+ | 1 | SIMPLE | user | NULL | ref | j_index | j_index | 5 | const | 1 | 100.00 | NULL | +----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------+
我們可以在某個特定的會話中,打開開關測試語句。
修改可見/不可見屬性
mysql> alter table user alter index i_index visible;
注意:
主鍵不可以設置不可見。因為也沒啥意義。
2.降序索引(descending index)
1)mysql8.0開始真正支持降序索引,在之前的mysql版本中也支持這種降序(DESC)索語法定義,但實際上mysql server會忽略這個定義,創建的還是升序索引(ASC)。
我們通常使用下面的語句來創建一個索引:
create index idx_t1_bcd on t1(b,c,d);
上面sql的意思是在t1表中,針對b,c,d三個字段創建一個聯合索引。
但是大家不知道的是,上面這個sql實際上和下面的這個sql是等價的:
create index idx_t1_bcd on t1(b asc,c asc,d asc);
2)目前只有innodb存儲引擎支持降序索引,只支持BTREE降序索引。
3)由於降序索引引入,mysql8.0不再對group by 操作進行隱式排序,如果需要進行order by指明處理。
4)降序索引帶來了性能的改進。
mysql5.7中
mysql> create table tablename1 (a int , b int, index idx1 (a asc,b desc)); Query OK, 0 rows affected (0.02 sec) mysql> show create table tablename1; +------------+----------------------------------------------------+ | Table | Create Table | +------------+----------------------------------------------------+ | tablename1 | CREATE TABLE `tablename1` ( `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL, KEY `idx1` (`a`,`b`) #雖然在語句中指定來a升序,b字段降序,但是這里都是默認升序 ) ENGINE=InnoDB DEFAULT CHARSET=latin1 | +------------+----------------------------------------------------+ 1 row in set (0.00 sec)
mysql8.0
mysql8> create table user2(a1 int,a2 int,index(a1 asc,a2 desc));
mysql> show create table user2\G;
*************************** 1. row ***************************
Table: user2
Create Table: CREATE TABLE `user2` (
`a1` int(11) DEFAULT NULL,
`a2` int(11) DEFAULT NULL,
KEY `a1` (`a1`,`a2` DESC) #a2 后面有個desc 是真正的降序
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)
插入一些數據
mysql8> insert into user2(a1,a2) values(1,100),(2,200),(3,150),(4,50); Query OK, 4 rows affected (0.02 sec) Records: 4 Duplicates: 0 Warnings: 0 mysql> select * from user2; +------+------+ | a1 | a2 | +------+------+ | 1 | 100 | | 2 | 200 | | 3 | 150 | | 4 | 50 | +------+------+
執行優化器,查詢使用a1升序,a2降序,查看索引使用情況。
mysql8> explain select * from user2 order by a1,a2 desc; +----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-------------+ | 1 | SIMPLE | user2 | NULL | index | NULL | a1 | 10 | NULL | 4 | 100.00 | Using index | 組合排序仍然可以使用到索引a1 +----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-------------+
可以使用到索引
mysql5.7
mysql> explain select * from user2 order by a1,a2 desc; +----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-----------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-----------------------------+ | 1 | SIMPLE | user2 | NULL | index | NULL | a1 | 10 | NULL | 4 | 100.00 | Using index; Using filesort | +----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-----------------------------+ 1 row in set, 1 warning (0.00 sec)
mysql5.7中雖然使用到了索引,但是還需要額外的排序操作,不能直接通過索引來得到我們鎖需要的順序。
當在mysql8.0執行相反查詢的時候。
mysql> explain select * from user2 order by a1 desc ,a2 ; +----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+----------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+----------------------------------+ | 1 | SIMPLE | user2 | NULL | index | NULL | a1 | 10 | NULL | 4 | 100.00 | Backward index scan; Using index | #從大到小的掃描 +----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+----------------------------------+
不僅用到了索引,還用到反向索引掃描。
上面就是新的降序索引帶來的性能的改進。
另外對group by不會默認的排序。隨機返回。
mysql8> select count(*) , a2 from user2 group by a2; +----------+------+ | count(*) | a2 | +----------+------+ | 1 | 100 | | 1 | 200 | | 1 | 150 | | 1 | 50 | +----------+------+ 4 rows in set (0.00 sec)
mysql8增加order by才有排序效果
mysql5.7默認增加order by排序
mysql5.7> select count(*) , a2 from user2 group by a2; +----------+------+ | count(*) | a2 | +----------+------+ | 1 | 50 | | 1 | 100 | | 1 | 150 | | 1 | 200 | +----------+------+
mysql8 增加order by 就和mysql5.7一樣,有排序的效果。
mysql8> select count(*) , a2 from user2 group by a2 order by a2; +----------+------+ | count(*) | a2 | +----------+------+ | 1 | 50 | | 1 | 100 | | 1 | 150 | | 1 | 200 | +----------+------+
3.函數索引
mysql8.0.13開始支持在索引中使用函數(表達式)的值,之前是使用列值,現在可以使用函數表達式的值使用索引,同時也支持降序索引,json數據的索引。之前版本的數據庫是沒法對json里各個節點的數據索引,函數索引是基於虛擬計算列功能來實現的。可以方便對json格式數據的查詢。
mysql> create table user3(c1 varchar(10),c2 varchar(10)); mysql> create index index1 on user3(c1); mysql> create index func_index on user3( (UPPER(c2)) ); #函數索引用大括號包起來
查看索引情況
mysql> show index from user3\G; *************************** 1. row *************************** Table: user3 Non_unique: 1 Key_name: index1 Seq_in_index: 1 Column_name: c1 Collation: A Cardinality: 0 Sub_part: NULL Packed: NULL Null: YES Index_type: BTREE Comment: Index_comment: Visible: YES Expression: NULL *************************** 2. row *************************** Table: user3 Non_unique: 1 Key_name: func_index Seq_in_index: 1 Column_name: NULL Collation: A Cardinality: 0 Sub_part: NULL Packed: NULL Null: YES Index_type: BTREE Comment: Index_comment: Visible: YES Expression: upper(`c2`) 2 rows in set (0.11 sec)
mysql> explain select * from user3 where upper(c1)='ABC'; +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+ | 1 | SIMPLE | user3 | NULL | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | Using where | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+ mysql> explain select * from user3 where upper(c2)='ABC'; +----+-------------+-------+------------+------+---------------+------------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------------+---------+-------+------+----------+-------+ | 1 | SIMPLE | user3 | NULL | ref | func_index | func_index | 43 | const | 1 | 100.00 | NULL | +----+-------------+-------+------------+------+---------------+------------+---------+-------+------+----------+-------+
分析:雖然有c1索引,但是還是全表掃描
針對json節點的索引
mysql> create table table_json (data json , index ((CAST(data->>'$.name' as char(30)) ))); mysql> show index from table_json; +------------+------------+------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+---------------------------------------------------------------------------------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression | +------------+------------+------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+---------------------------------------------------------------------------------------+ | table_json | 1 | functional_index | 1 | NULL | A | 0 | NULL | NULL | YES | BTREE | | | YES | cast(json_unquote(json_extract(`data`,_latin1\'$.name\')) as char(30) charset latin1) | +------------+------------+------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+---------------------------------------------------------------------------------------+
cast函數作為類型轉換 成char(30) ->>是新的json運算符,就是去name的值
mysql> explain select * from table_json where CAST(data->>'$.name' as char(30)) = 'aaa'; +----+-------------+------------+------------+------+------------------+------------------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+------------+------------+------+------------------+------------------+---------+-------+------+----------+-------+ | 1 | SIMPLE | table_json | NULL | ref | functional_index | functional_index | 33 | const | 1 | 100.00 | NULL | +----+-------------+------------+------------+------+------------------+------------------+---------+-------+------+----------+-------+
函數計算列
mysql> create table user3 (a1 varchar(10),a2 varchar(10)); mysql> desc user3; +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | a1 | varchar(10) | YES | | NULL | | | a2 | varchar(10) | YES | | NULL | | +-------+-------------+------+-----+---------+-------+
添加數據
mysql> alter table user3 add column a3 varchar(10) generated always as (upper(a1)); Query OK, 0 rows affected (0.03 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> insert into user3(a1,a2) values ('abc','abc'); Query OK, 1 row affected (0.01 sec) mysql> select * from user3; +------+------+------+ | a1 | a2 | a3 | +------+------+------+ | abc | abc | ABC | +------+------+------+
c3列是基於c1列大寫的形式計算出來的。
mysql> create index idx3 on user3(a3); Query OK, 0 rows affected (0.04 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> explain select * from user3 where upper(a1) = 'ABC'; +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+ | 1 | SIMPLE | user3 | NULL | ref | idx3 | idx3 | 43 | const | 1 | 100.00 | NULL | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
達到了與函數索引一樣的效果
通用表表達式(CTE):
即with子句,是sql語句的增強,通用表達式在sql2003標准(https://en.wikipedia.org/wiki/SQL:2003)中就引入了,很多主流數據庫都有該功能,mysql8.0也是擁有了該功能。
1.非遞歸CTE
派生表:select * from (select 1) as dt;
通用表表達式:with cte as (select 1)select * from cte;就相當於一個變量,在后面語句中使用。
mysql> select * from (select 1) as dt; +---+ | 1 | +---+ | 1 | +---+ mysql> with dt as (select 1) -> select * from dt; +---+ | 1 | +---+ | 1 | +---+
寫法更加清晰
mysql> with cte1(id) as (select 1), -> cte2(id) as (select id+1 from cte1) -> select * from cte1 join cte2; +----+----+ | id | id | +----+----+ | 1 | 2 | +----+----+
2.遞歸CTE
在查詢中引用自己的定義,使用RECURSIVE表示。和編程語言中的遞歸函數調用差不多。生成一些模擬數據也比較方便
mysql> with recursive cte (n) as -> ( -> select 1 -> union all -> select n + 1 from cte where n <10 -> ) -> select * from cte; +------+ | n | +------+ | 1 | | 2 | | 3 | | 4 | | 5 | | 6 | | 7 | | 8 | | 9 | | 10 | +------+
無限級分類表
mysql> create table employee (id int,name varchar(20),manager_id int); mysql> insert into employee values(29,'song',198),(72,'lucy',29),(123,'adil',692),(198,'join',333),(333,'yaml',NULL),(692,'cady',333),(700,'sarac',29); mysql> select * from employee; +------+-------+------------+ | id | name | manager_id | +------+-------+------------+ | 29 | song | 198 | | 72 | lucy | 29 | | 123 | adil | 692 | | 198 | join | 333 | | 333 | yaml | NULL | | 692 | cady | 333 | | 700 | sarac | 29 | +------+-------+------------+
實例
mysql> with recursive employee_paths(id,name,path) as -> ( -> select id,name,cast(id as char(200)) -> from employee -> where manager_id is null -> union all -> select e.id,e.name,concat(ep.path,',',e.id) -> from employee_paths as ep join employee as e -> on ep.id = e.manager_id -> ) -> select * from employee_paths order by path; +------+-------+----------------+ | id | name | path | +------+-------+----------------+ | 333 | yaml | 333 | | 198 | join | 333,198 | | 29 | song | 333,198,29 | | 700 | sarac | 333,198,29,700 | | 72 | lucy | 333,198,29,72 | | 692 | cady | 333,692 | | 123 | adil | 333,692,123 | +------+-------+----------------+
遞歸cte注意次數限制,遞歸表達式要包含一個終止遞歸的條件,避免死循環。
mysql8.0提供了參數cte_max_recursion_depth:最大遞歸深度, max_execution_time:sql語句最大執行時間。
mysql> with recursive cte(n) as -> ( -> select 1 -> union all -> select n+1 from cte -> ) -> select * from cte; ERROR 3636 (HY000): Recursive query aborted after 1001 iterations. Try increasing @@cte_max_recursion_depth to a larger value.
在第1001次阻斷
mysql> show variables like 'cte_max%'; +-------------------------+-------+ | Variable_name | Value | +-------------------------+-------+ | cte_max_recursion_depth | 1000 | +-------------------------+-------+ mysql> set session cte_max_recursion_depth=10;
0表示無限制 1000毫秒=1秒
通用表表達式和派生表很類似,就像語句級別的臨時表和試圖,用派生表的地方就可以用通用表表達式,語意更加清晰點。
cte可以在查詢中多次引用,也可以引用其他的cte
cte支持select/insert/update/delete等語句
比如使用遞歸CTE生成斐波那契數列。
窗口函數(window function)
也稱呼為分析函數,為sql語句提供強大的數據分析功能,可以通過sql語句做一些數據分析。
窗口函數與分組聚合函數類似,但是每一行數據都生成一個結果,就是主要對數據進行分組group by操作,然后對每一個組聚合產生一個結果,比如組內的平均數,組內的組合等等。窗口函數與之相似也可以分組,不同的地方在於,針對分組內的每一個行都會生成相應的一個結果,所以結果和行數上相同的。
窗口函數定義
over是關鍵字,用來指定函數執行的窗口范圍,內部可以指定三個選項,第一個是先對數據進行分組,第二個是對分組的數據進行排序,第三個進一步操作。
常見聚合窗口函數:SUM / AVG / COUNT / MAX / MIN 等等。
mysql> insert into sales values (2000,'Finland','Computer',1500),(2001,'USA','Phone',1200),(2001,'Finland','Phone',10),(2000,'India','Calculator',75),
(2001,'USA','TV',150),(2000,'India','Computer',1200),(2000,'USA','Calculator',5),(2000,'USA','Computer',1500),(2000,'Finland','Phone',100),
(2001,'USA','Calculator',50),(2001,'USA ',' TV',100); mysql> select * from sales; +------+---------+------------+--------+ | year | country | product | profit | +------+---------+------------+--------+ | 2000 | Finland | Computer | 1500 | | 2001 | USA | Phone | 1200 | | 2001 | Finland | Phone | 10 | | 2000 | India | Calculator | 75 | | 2001 | USA | TV | 150 | | 2000 | India | Computer | 1200 | | 2000 | USA | Calculator | 5 | | 2000 | USA | Computer | 1500 | | 2000 | Finland | Phone | 100 | | 2001 | USA | Calculator | 50 | | 2001 | USA | Computer | 1500 | | 2000 | India | Calculator | 75 | | 2001 | USA | TV | 100 | +------+---------+------------+--------+
mysql> select country,sum(profit) as country_profit -> from sales -> group by country -> order by country; +---------+----------------+ | country | country_profit | +---------+----------------+ | Finland | 1610 | | India | 1350 | | USA | 4505 |
mysql> select year,country,product,profit, -> sum(profit) OVER (partition by country) as country_profit -> from sales -> order by country,year,product,profit; +------+---------+------------+--------+----------------+ | year | country | product | profit | country_profit | +------+---------+------------+--------+----------------+ | 2000 | Finland | Computer | 1500 | 1610 | | 2000 | Finland | Phone | 100 | 1610 | | 2001 | Finland | Phone | 10 | 1610 | | 2000 | India | Calculator | 75 | 1350 | | 2000 | India | Calculator | 75 | 1350 | | 2000 | India | Computer | 1200 | 1350 | | 2000 | USA | Calculator | 5 | 4505 | | 2000 | USA | Computer | 1500 | 4505 | | 2001 | USA | Calculator | 50 | 4505 | | 2001 | USA | Computer | 1500 | 4505 | | 2001 | USA | Phone | 1200 | 4505 | | 2001 | USA | TV | 100 | 4505 | | 2001 | USA | TV | 150 | 4505 | +------+---------+------------+--------+----------------+
使用窗口函數,每一行都出現,保留了原來的數據結構,在原來基礎上增加一些分析出來結果列,而不像分組聚合函數那樣。分析數據可以使用
前四列是原始數據
窗口函數不需要在加group by了。
專用窗口函數
獲取排名函數
ROW_NUMBER() / RANK() /DENSE_RANK() / PERCENT_RANK()
FIRST_VALUE() / LAST_VALUE() / LEAD() / LAG()
CUME_DIST() / NTH_VALUE() / NTILE()
mysql> insert into numbers values (1),(1),(2),(3),(3),(3),(3),(4),(4),(5); Query OK, 10 rows affected (0.01 sec) Records: 10 Duplicates: 0 Warnings: 0 mysql> select * from numbers; +------+ | nums | +------+ | 1 | | 1 | | 2 | | 3 | | 3 | | 3 | | 3 | | 4 | | 4 | | 5 | +------+
排名
mysql> select nums , row_number() OVER (order by nums) as 'row_number' from numbers; +------+------------+ | nums | row_number | +------+------------+ | 1 | 1 | | 1 | 2 | | 2 | 3 | | 3 | 4 | | 3 | 5 | | 3 | 6 | | 3 | 7 | | 4 | 8 | | 4 | 9 | | 5 | 10 | +------+------------+
mysql> select nums, -> first_value(nums) over (order by nums) as 'first', -> lead(nums,1) over (order by nums) as 'lead' -> from numbers; +------+-------+------+ | nums | first | lead | +------+-------+------+ | 1 | 1 | 1 | | 1 | 1 | 2 | | 2 | 1 | 3 | | 3 | 1 | 3 | | 3 | 1 | 3 | | 3 | 1 | 3 | | 3 | 1 | 4 | | 4 | 1 | 4 | | 4 | 1 | 5 | | 5 | 1 | NULL | +------+-------+------+
Innodb存儲引擎改進
Innodb存儲引擎成為mysql默認存儲引擎后,功能一直在改進,mysql8.0中innodb的功能也得到增強。
1.集成數據字典
mysql8.0重新重構了數據字典
1)刪除了之前元數據文件關於數據庫等信息,例如.frm .opt等基於文件等數據庫信息。
mysql5.7
root@4a68d9279589:/# cd /var/lib/mysql root@4a68d9279589:/var/lib/mysql# ls auto.cnf client-cert.pem ib_logfile0 ibtmp1 private_key.pem server-key.pem ca-key.pem client-key.pem ib_logfile1 mysql public_key.pem sys ca.pem ib_buffer_pool ibdata1 performance_schema server-cert.pem testDB root@4a68d9279589:/var/lib/mysql# ls mysql columns_priv.MYD gtid_executed.ibd proc.MYI slow_log.CSV columns_priv.MYI help_category.frm proc.frm slow_log.frm columns_priv.frm help_category.ibd procs_priv.MYD tables_priv.MYD db.MYD help_keyword.frm procs_priv.MYI tables_priv.MYI db.MYI help_keyword.ibd procs_priv.frm tables_priv.frm db.frm help_relation.frm proxies_priv.MYD time_zone.frm db.opt help_relation.ibd proxies_priv.MYI time_zone.ibd engine_cost.frm help_topic.frm proxies_priv.frm time_zone_leap_second.frm engine_cost.ibd help_topic.ibd server_cost.frm time_zone_leap_second.ibd event.MYD innodb_index_stats.frm server_cost.ibd time_zone_name.frm event.MYI innodb_index_stats.ibd servers.frm time_zone_name.ibd event.frm innodb_table_stats.frm servers.ibd time_zone_transition.frm func.MYD innodb_table_stats.ibd slave_master_info.frm time_zone_transition.ibd func.MYI ndb_binlog_index.MYD slave_master_info.ibd time_zone_transition_type.frm func.frm ndb_binlog_index.MYI slave_relay_log_info.frm time_zone_transition_type.ibd general_log.CSM ndb_binlog_index.frm slave_relay_log_info.ibd user.MYD general_log.CSV plugin.frm slave_worker_info.frm user.MYI general_log.frm plugin.ibd slave_worker_info.ibd user.frm gtid_executed.frm proc.MYD slow_log.CSM
mysql8.0
root@8de6e5139f7d:/# cd /var/lib/mysql/ root@8de6e5139f7d:/var/lib/mysql# ls mysql general_log.CSM general_log.CSV general_log_197.sdi slow_log.CSM slow_log.CSV slow_log_198.sdi root@8de6e5139f7d:/var/lib/mysql# ls -l mysql total 28 -rw-r----- 1 mysql mysql 35 Jul 15 11:19 general_log.CSM -rw-r----- 1 mysql mysql 0 Jul 15 11:19 general_log.CSV -rw-r----- 1 mysql mysql 5561 Jul 15 11:19 general_log_197.sdi -rw-r----- 1 mysql mysql 35 Jul 15 11:19 slow_log.CSM -rw-r----- 1 mysql mysql 0 Jul 15 11:19 slow_log.CSV -rw-r----- 1 mysql mysql 11786 Jul 15 11:19 slow_log_198.sdi
mysql8.0主要存放在這個目錄下
root@8de6e5139f7d:/var/lib/mysql# ls #innodb_temp binlog.000003 ca-key.pem ib_buffer_pool ibtmp1 private_key.pem sys auto.cnf binlog.000004 ca.pem ib_logfile0 mysql public_key.pem testDB binlog.000001 binlog.000005 client-cert.pem ib_logfile1 mysql.ibd server-cert.pem undo_001 binlog.000002 binlog.index client-key.pem ibdata1 performance_schema server-key.pem undo_002 root@8de6e5139f7d:/var/lib/mysql# ls -l mysql.ibd -rw-r----- 1 mysql mysql 31457280 Jul 15 12:50 mysql.ibd
mysql8將系統表(mysql)和數據字典全部改為innodb。
2.原子ddl操作,基於innodb事務特性。
由於采用了新的數據字典, MySQL 8.0 現在支持原子數據定義語句 (Atomic DDLs)。 這意味着執行 DDL 時,數據字典更新,存儲引擎操作以及二進制日志文件中的寫入操作會合並到單個原子事務中,該事務要么完全執行,要么根本不執行。這提高了 DDL 的穩定性保證未完成的 DDL 不會留下任何不完整的數據。
mysql8.0開始支持原子DDL操作,原子DDL操作又很多種,但其中與表相關的原子DDL只支持Innodb存儲引擎,比如創建表,刪除表。一個原子DDL操作包括:更新數據字典,存儲引擎層的操作,在binlog二進制日志中記錄DDL操作,
支持與表相關的DDL:數據庫,表空間,表,索引的create,alter,drop以及truncate table。
支持的其他DDL:存儲過程,觸發器,視圖,UDF(用戶定義函數)的create,drop以及alter語句。
支持賬戶管理相關DDL:用戶和角色的create,alter,drop以及適用的rename,以及GRANT(授權)和REVOKE(撤銷授權)語句。
mysql> show tables; +------------------+ | Tables_in_testDB | +------------------+ | table1 | +------------------+ 1 row in set (0.00 sec) mysql> drop table table1,table2; ERROR 1051 (42S02): Unknown table 'testDB.table2' mysql> show tables; Empty set (0.00 sec)
雖然錯誤,但是t1表已經被刪除了,所以這個操作不具有原子性。
在mysql8.0進行相同操作
mysql> show tables; +------------------+ | Tables_in_testDB | +------------------+ | employee | | user | | user2 | | user3 | +------------------+ 4 rows in set (0.00 sec) mysql> drop table user3,user4; ERROR 1051 (42S02): Unknown table 'testDB.user4' mysql> show tables; +------------------+ | Tables_in_testDB | +------------------+ | employee | | user | | user2 | | user3 | 表user3依然存在 +------------------+
新版本功能對復制的影響:
主從復制時候,主節點上mysql5.7,從節點上mysql8.0,由於實現原理不一樣,主節點drop語句有可能成功一部分,而mysql8.0會全部失敗,可以通過判斷drop if exists t1,t2。
3.自增列持久化
mysql5.7及早期版本,Innodb自增列計數器(AUTH_INCREMENT)的只值存儲在內存中,它值的更新只會在內存中更新,當系統出現故障或者重啟,它需要重新去掃描表中的自增列,找到當前最大列,然后基於這個值在自增,在某些情況下它這個值有可能是之前使用過的值,也就是說會出現重復的值,對於主鍵來說是不允許有重復的值。
基於以上原因mysql8.0做了修改,mysql8.0每次變化時將自增計數器的最大值寫入redo log,同時每次在檢查點將其寫入引擎私有的系統表。系統在下次重啟或者恢復時候可以找到曾經使用過的最大值,避免新生成的值和以前的值重復的情況。
mysql57> create table table1(id int auto_increment primary key, name varchar(10)); Query OK, 0 rows affected (0.02 sec) mysql57> insert into table1(name) values('tom'),('lucy'),('make'); Query OK, 3 rows affected (0.02 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql57> select * from table1; +----+------+ | id | name | +----+------+ | 1 | tom | | 2 | lucy | | 3 | make | +----+------+ 3 rows in set (0.00 sec) mysql57> delete from table1 where id=3; Query OK, 1 row affected (0.02 sec) mysql57> select * from table1; +----+------+ | id | name | +----+------+ | 1 | tom | | 2 | lucy | +----+------+ 2 rows in set (0.01 sec)
刪除並重啟
songguojundeMBP:~ songguojun$ docker stop song-mysql57 song-mysql57 songguojundeMBP:~ songguojun$ docker start song-mysql57 song-mysql57 songguojundeMBP:~ songguojun$ docker exec -it song-mysql57 /bin/bash
mysql> select * from table1; +----+------+ | id | name | +----+------+ | 1 | tom | | 2 | lucy | +----+------+ 2 rows in set (0.00 sec) mysql> insert into table1(name) values('salary'); Query OK, 1 row affected (0.00 sec) mysql> select * from table1; +----+--------+ | id | name | +----+--------+ | 1 | tom | | 2 | lucy | | 3 | salary | +----+--------+ 3 rows in set (0.00 sec) mysql> update table1 set id=5 where name = 'tom'; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from table1; +----+--------+ | id | name | +----+--------+ | 2 | lucy | | 3 | salary | | 5 | tom | +----+--------+ 3 rows in set (0.00 sec) mysql> insert into table1(name) values ('song'); Query OK, 1 row affected (0.01 sec) mysql> select * from table1; +----+--------+ | id | name | +----+--------+ | 2 | lucy | | 3 | salary | | 4 | song | | 5 | tom | +----+--------+ 4 rows in set (0.00 sec) mysql> insert into table1(name) values ('song2'); ERROR 1062 (23000): Duplicate entry '5' for key 'PRIMARY'
接下來看mysql8.0是如何解決這個問題的
按照上面的步驟操作即可
mysql> show variables like 'innodb_autoinc%'; +--------------------------+-------+ | Variable_name | Value | +--------------------------+-------+ | innodb_autoinc_lock_mode | 2 | +--------------------------+-------+
參數解釋:
mysql8.0這個值默認是2 mysql5.7這個默認值是1, 2是交叉模式,這個鎖是交叉生成的,1表示連續模式,這兩個值差別在於復制的時候,比如說之前 復制說基於語句的復制,比如一條語句生成10條記錄,在這條語句中生成的記錄都是順序的,如果從主節點復制到從節點也要保證說順序的話就要使用這個值為1,可以保證語句級別的增長都是連續的,最新的復制模式改為行的復制模式,意味着將行的實際數據值復制,所以不關心自增列是怎么生成的,也不能保證生成的id一致性,因為不像之前版本加上鎖,這樣的好處可以支持更高的並發和擴展。如果還想要之前那樣基於語句就要注意這個參數設置下。
4.死鎖檢查控制
死鎖:有兩個事務都需要對數據進行修改,修改的過程中都需要等待對方釋放資源,由於互相沒有感知,在沒有外界的介入下它們會一直等待下去,就形成了死鎖。mysql后台有個死鎖檢測程序,在后台發現了會讓一個事務失敗,讓另一個事務進行下去,當然死鎖檢測需要一定的代價,需要占用一定的系統資源。
mysql8.0及mysql5.7.15增加了一個新的動態變量,用於控制系統是否執行Innodb的死鎖檢測。
innodb_deadlock_detect 默認情況說打開的,會進行死鎖檢測。
對於高並發的系統,死鎖檢測會占用系統資源,所以禁用死鎖檢測可能會帶來性能的提高。
mysql8> show variables like 'innodb_deadlock_detect'; +------------------------+-------+ | Variable_name | Value | +------------------------+-------+ | innodb_deadlock_detect | ON | +------------------------+-------+
下面開啟兩個事務,然后在兩個事務之間互相鎖定各自的資源,來等待對方的鎖。
先創建一張表
mysql> create table table_t1 (i int); Query OK, 0 rows affected (0.03 sec) mysql> insert into table_t1(i) values(1); Query OK, 1 row affected (0.00 sec)
事務1 | 說明 | 事務2 | 說明 |
mysql> start transaction; Query OK, 0 rows affected (0.01 sec) mysql> select * from table_t1 where i=1 for share; +------+ | i | +------+ | 1 | +------+ 1 row in set (0.00 sec) |
for share獲取記錄上的一個共享鎖 | mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> delete from table_t1 where i=1; |
刪除需要排它鎖 ,由於前面一個會話已經占用了一個共享鎖,這時候在等待資源的釋放。 |
mysql> delete from table_t1 where i=1; Query OK, 1 row affected (0.01 sec) |
第二個窗口 刪除時候也需要排它鎖,這個時候它也需要第二個會話事務釋放鎖才能進行下去,發現了死鎖 。 因為系統發現了死鎖並解除死鎖,這里可以進行下去。 |
mysql> delete from table_t1 where i=1; ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction |
死鎖被發現,系統將它會滾掉,讓第一個事務進行下去 |
mysql> select * from table_t1; Empty set (0.00 sec) |
數據被刪除 |
以上是innodb_deadlock_detect默認打開情況下掉行為。
那么我們將這個參數關閉測試看看如何處理死鎖的
mysql> set global innodb_deadlock_detect = off; #關閉死鎖檢測 Query OK, 0 rows affected (0.00 sec) mysql> show variables like 'innodb_deadlock_detect'; +------------------------+-------+ | Variable_name | Value | +------------------------+-------+ | innodb_deadlock_detect | OFF | +------------------------+-------+
還有一個參數 鎖等待超時 這個參數會影響實驗
mysql> show variables like 'innodb_lock_wait%'; +--------------------------+-------+ | Variable_name | Value | +--------------------------+-------+ | innodb_lock_wait_timeout | 50 | #默認是50秒 +--------------------------+-------+ 1 row in set (0.00 sec) mysql> set global innodb_lock_wait_timeout=5; Query OK, 0 rows affected (0.00 sec)
50秒設置為5秒
事務1 | 說明 | 事務2 | 說明 |
mysql> start transaction; Query OK, 0 rows affected (0.01 sec) mysql> select * from table_t1 where i=1 for share; +------+ | i | +------+ | 1 | +------+ 1 row in set (0.00 sec) |
mysql> start transaction;
|
||
mysql> delete from table_t1 where i=1; |
執行刪除操作,在等待中 | ||
mysql> delete from table_t1 where i=1; Query OK, 1 row affected (31.99 sec) |
這個事務也在等待中, 如果死鎖檢測打開,就會提示死鎖錯誤信息,但是這里沒有提示 |
||
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction | 通過超時來回滾事務,讓事務二失敗 |
注意:
1.死鎖檢測關閉的前提是我們系統不會發生死鎖的情況,所以在我們寫代碼寫sql語句要注意點死鎖的發生。
2.鎖等待超時時間不要設置太長,防止對系統的影響。
5.鎖定語句選項
mysql里面有兩種為查詢語句加鎖的語句
1. select....... for update
2. select ....... for share
這兩個語句分別是為我們查詢出來的語句加上共享鎖和排它鎖,如果查出來的數據在其他事物中以及占用了相應的鎖,那么我們的語句需要進行相應的等待直到響應的事務釋放鎖直到超時。
mysql8.0為這兩個語句增加了兩個新的選項。
1) NOWAIT:如果請求的行被其他事物鎖定,語句立即返回,不等待。
2)SKIP LOCKED:從返回的結果中移除鎖定的行,只返回沒有被鎖定的行。應用場景,比如在線票務系統,有很多並發請求,如果票被其他線程占用,這時候可以選擇不等待,返回可以使用的票。
mysql> create table table6 (i int ,primary key(i)); mysql> insert into table6 values (1),(2),(3);
事務1 | 說明 | 事務2 | 說明 |
mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> update table6 set i=0 where i=2; |
加上排它鎖,不進行提交 | ||
start transaction; select * from table6 where i=2 for update; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction |
由於前面會話占用了鎖,會一直等待直到語句超時 | ||
mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from table6 where i=2 for update nowait; ERROR 3572 (HY000): Statement aborted because lock(s) could not be acquired immediately and NOWAIT is set. |
如果加上nowait選項選擇不等待鎖會返回一個錯誤,相應數據被占用,直接返回錯誤。 | ||
mysql> select * from table6 for update skip locked; +---+ | i | +---+ | 1 | | 3 | +---+ 2 rows in set (0.00 sec) |
加上skip locked選項選擇跳過鎖定的數據,返回別的沒被鎖定的數據。 |
注意:
1.這兩個參數鎖針對行級鎖。
2.這兩個鎖針對行級鎖,對每一條行記錄起作用。
主從復制,主從當時運行環境情況的各不一樣,導致數據的不確定性,對於語句級別會帶來不一致性問題。
3.簡化了INFORMATION_SCHEMA的實現,提高了訪問性能。
4.針對innodb存儲引擎提供了序列化字典信息(SDL)的支持,以及ibd2sdi工具。SDI是一個文本文件,存儲了數據字典信息。ibd2sdi工具可以將innodb相關表信息導出成文本信息。
root@f488b1c2586a:/# cd /var/lib/mysql/ root@f488b1c2586a:/var/lib/mysql# ls #innodb_temp ca.pem ibdata1 private_key.pem undo_001 auto.cnf client-cert.pem ibtmp1 public_key.pem undo_002 binlog.000001 client-key.pem mysql server-cert.pem binlog.000002 ib_buffer_pool mysql.ibd server-key.pem binlog.index ib_logfile0 mysqld-auto.cnf sys ca-key.pem ib_logfile1 performance_schema testDB root@f488b1c2586a:/var/lib/mysql# cd testDB/ root@f488b1c2586a:/var/lib/mysql/testDB# ls numbers.ibd sales.ibd table_json.ibd user2.ibd user3.ibd
root@f488b1c2586a:/var/lib/mysql/testDB# ibd2sdi table_json.ibd > sales.ibd root@f488b1c2586a:/var/lib/mysql/testDB# ls numbers.ibd sales.ibd table_json.ibd user2.ibd user3.ibd root@f488b1c2586a:/var/lib/mysql/testDB# cat sales.ibd ["ibd2sdi" , { "type": 1, "id": 342, "object": { "mysqld_version_id": 80016, "dd_version": 80016, "sdi_version": 80016, "dd_object_type": "Table", "dd_object": { "name": "table_json", "mysql_version_id": 80016, "created": 20190722122718, "last_altered": 20190722122718, "hidden": 1, "options": "avg_row_length=0;encrypt_type=N;key_block_size=0;keys_disabled=0;pack_record=1;stats_auto_recalc=0;stats_sample_pages=0;", "columns": [ { "name": "data", "type": 31, "is_nullable": true, "is_zerofill": false, "is_unsigned": false, "is_auto_increment": false, "is_virtual": false, "hidden": 1, "ordinal_position": 1, "char_length": 4294967295, "numeric_precision": 0, "numeric_scale": 0, "numeric_scale_null": true, "datetime_precision": 0, "datetime_precision_null": 1, "has_no_default": false, "default_value_null": true, "srs_id_null": true, "srs_id": 0, "default_value": "", "default_value_utf8_null": true, "default_value_utf8": "", "default_option": "", "update_option": "", "comment": "", "generation_expression": "", "generation_expression_utf8": "", "options": "interval_count=0;", "se_private_data": "table_id=1066;", "column_key": 1, "column_type_utf8": "json", "elements": [], "collation_id": 63, "is_explicit_collation": true },
比如當數據表損壞的時候可以用這些文本信息恢復。
由於新的數據字典的引入,會導致之前使用上的差異,例如innodb_read_only影響所有所有的存儲引擎。數據字典不可見,不能直接修改和查詢。
6.索引跳躍掃描(index skip scan)
mysql> CREATE TABLE table_scan (i1 int, i2 int , PRIMARY KEY(i1, i2)); mysql> INSERT INTO table_scan VALUES (1,1), (1,2), (1,3), (1,4), (1,5),(2,1), (2,2), (2,3), (2,4), (2,5); mysql> INSERT INTO table_scan SELECT i1, i2 + 5 FROM table_scan; mysql> INSERT INTO table_scan SELECT i1, i2 + 10 FROM table_scan; mysql> INSERT INTO table_scan SELECT i1, i2 + 20 FROM table_scan; mysql> INSERT INTO table_scan SELECT i1, i2 + 40 FROM table_scan; mysql> EXPLAIN SELECT i1, i2 FROM table_scan WHERE i2 > 40; +----+-------------+------------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+------------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+ | 1 | SIMPLE | table_scan | NULL | index | PRIMARY | PRIMARY | 8 | NULL | 160 | 33.33 | Using where; Using index | +----+-------------+------------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+mysql> ANALYZE TABLE table_scan; +-------------------+---------+----------+----------+ | Table | Op | Msg_type | Msg_text | +-------------------+---------+----------+----------+ | testDB.table_scan | analyze | status | OK | +-------------------+---------+----------+----------+ mysql> EXPLAIN SELECT i1, i2 FROM table_scan WHERE i2 > 40; +----+-------------+------------+------------+-------+---------------+---------+---------+------+------+----------+----------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+------------+------------+-------+---------------+---------+---------+------+------+----------+----------------------------------------+ | 1 | SIMPLE | table_scan | NULL | range | PRIMARY | PRIMARY | 8 | NULL | 53 | 100.00 | Using where; Using index for skip scan | +----+-------------+------------+------------+-------+---------------+---------+---------+------+------+----------+----------------------------------------+
mysql> show variables like 'optimizer_trace%'; +------------------------------+----------------------------------------------------------------------------+ | Variable_name | Value | +------------------------------+----------------------------------------------------------------------------+ | optimizer_trace | enabled=off,one_line=off | | optimizer_trace_features | greedy_search=on,range_optimizer=on,dynamic_range=on,repeated_subselect=on | | optimizer_trace_limit | 1 | | optimizer_trace_max_mem_size | 1048576 | | optimizer_trace_offset | -1 | +------------------------------+----------------------------------------------------------------------------+
開啟 optimizer_trace
SET optimizer_trace='enabled=on';
執行需要執行的 sql
SELECT i1, i2 FROM table_scan WHERE i2 > 40;
可以從optimizer trace里看到如何選擇的skip scan
mysql> select trace from `information_schema`.`optimizer_trace`\G; *************************** 1. row *************************** trace: { "steps": [ { "join_preparation": { "select#": 1, "steps": [ { "expanded_query": "/* select#1 */ select `table_scan`.`i1` AS `i1`,`table_scan`.`i2` AS `i2` from `table_scan` where (`table_scan`.`i2` > 40)" } ] } }, {
#此處省略.....
"group_index_range": { "chosen": false, "cause": "not_group_by_or_distinct" }, "skip_scan_range": { "potential_skip_scan_indexes": [ { "index": "PRIMARY", "tree_travel_cost": 0.4, "num_groups": 3, "rows": 53, "cost": 10.625 } ] }, "best_skip_scan_summary": { "type": "skip_scan", "index": "PRIMARY", "key_parts_used_for_access": [ "i1", "i2" ], "range": [ "40 < i2" ], "chosen": true }, "rows_for_plan": 53, "cost_for_plan": 10.625, "chosen": true } } } ]
#此處省略..... } }, { "join_execution": { "select#": 1, "steps": [ ] } } ] } 1 row in set (0.00 sec)
mysql> show variables like 'optimizer_switch%'; +------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Variable_name | Value | +------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | optimizer_switch | index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,
index_condition_pushdown=on,mrr=on,mrr_cost_based=on,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,
firstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on,use_index_extensions=on,condition_fanout_filter=on,derived_merge=on,
use_invisible_indexes=off,skip_scan=on | +------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
innodb其他的改進
1)支持部門快速DDL alter table .......ALGORITHM=INSTANT
比如快速增加某一列,可以使用INSTANT這種算法。就是修改相應數據字典的信息,而不是像以前重新創建一張表進行數據復制,這個比以前快很多,尤其線上系統非常實用。(這個功能鎖騰訊開發合並到主分支上)
2)innodb臨時表使用的共享臨時表空間ibtmp1,改變之前使用分散的表空間的問題,存儲在ibtmp1。臨時表使用完就刪除了,現在統一放在ibtmp1,統一的維護。
3)新增靜態變量innodb_dedicated_server,如果有一台服務器是專門用於mysql數據庫的,可以打開這個配置變量,系統會自動配置innodb相關等內存配置參數, 自動配置Innodb內存參數:innodb_buffer_pool_size/innodb_log_file_size等,盡量合理占用系統資源來提高系統等利用率。
4)新增表INFORMATION_SCHEMA.INNODB_CACHED_INDEXES顯示每個索引緩存在innodb緩沖池中的索引頁數,通過這個參數也可以了解索引緩存使用情況。
5)新增視圖INFORMATION_SCHEMA.INNODB_TABLESPACES_BRIEF,為innodb表空間提供相關元數據信息。
6)mysql8.0默認創建2個undo表空間,不再使用系統表空間。以前undo表空間會占用系統表空間資源,單獨存儲會是系統表空間獨立點。
7)支持alter tablespace......rename to重命名通用表空間。
8)支持使用innodb_directories選項在服務器停止時將表空間文件移動到新的位置。這個參數是表空間文件所在的路徑,通過修改參數
9)innodb表空間加密特性支持重做日志和撤銷日志,innodb_undo_log_encrypt和innodb_undo_log_encrypt參數。加密會更加安全。
JSON增強
json數據格式和關系型數據庫的數據格式是不一樣的,它是一個結構不固定的數據結構方式,也算是屬於nosql范疇,mysql針對json不斷的增強。mysql8.0關於json有了一些增強,主要是增加了一些內置的運算符和數據處理函數。
1.內聯路徑操作符
主要用於獲取json對象在某一些節點或者某些路徑上的數據。
內聯操作符的表達式:
column->>path 等價於JSON_UNQUOTE( column -> path) JSON_UNQUOTE(JSON_EXTRACT(column,path))
示例演示
mysql> with doc(data) as -> ( select json_object('id','1','name','tom')) #創建一個json對象 有兩個字段 id name -> select json_unquote(data->'$.name') from doc; #獲得節點name的值 +------------------------------+ | json_unquote(data->'$.name') | +------------------------------+ | tom | +------------------------------+
還有一種方式也是之前版本支持的,這里實用另外一個函數json_extract
mysql> with doc(data) as
-> ( select json_object('id','1','name','tom'))
-> select json_unquote(json_extract(data,'$.name')) from doc; +-------------------------------------------+ | json_unquote(json_extract(data,'$.name')) | +-------------------------------------------+ | tom | +-------------------------------------------+
以上是mysql8之前寫法
下面看mysql8.0內聯路徑操作符的寫法
mysql> with doc(data) as ( select json_object('id','1','name','tom')) select data->>'$.name' from doc; +-----------------+ | data->>'$.name' | +-----------------+ | tom | +-----------------+
mysql8.0還擴展了這個路徑表達式的語法,可以支持范圍的操作。
mysql> select json_extract('["a","b","c"]','$[1]'); +--------------------------------------+ | json_extract('["a","b","c"]','$[1]') | +--------------------------------------+ | "b" | +--------------------------------------+ mysql> select json_extract('["a","b","c"]','$[1 to 3]'); #這個是新版本寫法 支持范圍查找 +-------------------------------------------+ | json_extract('["a","b","c"]','$[1 to 3]') | +-------------------------------------------+ | ["b", "c"] | +-------------------------------------------+
2.json聚合函數
可以將表中列的數據聚合成對應的json數組或者json對象。
mysql8.0增加了兩個聚合函數,mysql5.7.22也增加了相同的函數。
1)JSON_ARRAYAGG() ,用於將多行數據組合生成JSON數組。
2) JSON_OBJECTAGG(),用於生成json對象。
示例演示
mysql> create table jsontable(i int,attribute varchar(100),value varchar(100)); mysql> insert into jsontable values(2,'color','red'),(2,'fabric','silk'),(3,'color','green'),(3,'shape','square');
json聚合函數和普通聚合函數是一樣的。
先看數組聚合函數
mysql> select i,json_arrayagg(attribute) as attributes from jsontable group by i; +------+---------------------+ | i | attributes | +------+---------------------+ | 2 | ["color", "fabric"] | | 3 | ["color", "shape"] | +------+---------------------+
json_objectagg支持多個列
mysql> select i,json_objectagg(attribute,value) as attributes from jsontable group by i; +------+---------------------------------------+ | i | attributes | +------+---------------------------------------+ | 2 | {"color": "red", "fabric": "silk"} | | 3 | {"color": "green", "shape": "square"} | +------+---------------------------------------+
如果存在重復值是如何處理的
mysql> insert into jsontable values(3,'color','yellow'); mysql> select * from jsontable; +------+-----------+--------+ | i | attribute | value | +------+-----------+--------+ | 2 | color | red | | 2 | fabric | silk | | 3 | color | green | | 3 | shape | square | | 3 | color | yellow | +------+-----------+--------+ mysql> select i,json_objectagg(attribute,value) as attributes from jsontable group by i; +------+----------------------------------------+ | i | attributes | +------+----------------------------------------+ | 2 | {"color": "red", "fabric": "silk"} | | 3 | {"color": "yellow", "shape": "square"} | #最后面的值覆蓋前面的值 +------+----------------------------------------+
3.json實用函數
用於對json對象的輸出或者獲取json所占用的存儲空間。
mysql8.0 (mysql5.7.22)增加了JSON_PRETTY()。這個函數用於在輸出json對象內容的時候進行格式化或者美化的輸出。
mysql8.0 (mysql5.7.22)增加了JSON_STORAGE_SIZE(),返回json數據所占用的空間大小。
JSON_STORAGE_FREE(),用於更新某些json列之后,相應的一些字段它可能釋放的存儲空間。
mysql> select json_object('id','1','name','jack'); #先構造一個json對象 +-------------------------------------+ | json_object('id','1','name','jack') | +-------------------------------------+ | {"id": "1", "name": "jack"} | +-------------------------------------+ 1 row in set (0.00 sec) mysql> select json_pretty(json_object('id','1','name','jack')); #內容一樣,格式被處理了 +--------------------------------------------------+ | json_pretty(json_object('id','1','name','jack')) | +--------------------------------------------------+ | { "id": "1", "name": "jack" } |
mysql> create table json_table2 (j_field varchar(200)); mysql> insert into json_table2 values('{"a":1000,"b":"aaa","c":"[1,2,3,4,5,6]"}'); mysql> select * from json_table2; +------------------------------------------+ | j_field | +------------------------------------------+ | {"a":1000,"b":"aaa","c":"[1,2,3,4,5,6]"} | +------------------------------------------+ 1 row in set (0.00 sec) mysql> select j_field , json_storage_size(j_field) from json_table2; #查看空間大小 +------------------------------------------+----------------------------+ | j_field | json_storage_size(j_field) | +------------------------------------------+----------------------------+ | {"a":1000,"b":"aaa","c":"[1,2,3,4,5,6]"} | 47 | #47個字節 +------------------------------------------+----------------------------+
mysql> select j_field , json_storage_free(j_field) from json_table2; +------------------------------------------+----------------------------+ | j_field | json_storage_free(j_field) | +------------------------------------------+----------------------------+ | {"a":1000,"b":"aaa","c":"[1,2,3,4,5,6]"} | 0 | #默認上面表數據沒有更新 所以這里數據顯示釋放的空間為0 +------------------------------------------+----------------------------+
mysql> update json_table2 set j_field = json_set(j_field,"$.a",10,"$.b","bb","$.c",1);
4.json合並函數
主要是將兩個json對象合並成一個。
5.json表函數
和json聚合函數執行相反的操作。將這種json對象擴展成關系型數據表行和列組織的數據形式。
四.mysql8.0其它的新的特性
1. 默認字符集由latin1變為utf8mb4。
2. 統計直方圖,是一種統計信息,統計表中字段各值的分布情況。由於有時候查詢優化器會走不到最優的執行計划,所以利用統計直方圖,用戶可以對一張表的一列做數據分布的統計,尤其是針對沒有索引的字段。這可以幫助查詢優化器找到更優的執行計划。統計直方圖的主要使用場景是用來計算字段選擇性。
3.新增mysql-auto.cnf文件,比my.cnf具有更高優先權。
五.mysql8.0去掉的特性
有增加的新特性,也會有去掉不適用老的功能
1.取消 Query Cache
現在性能審計中第一件事就是禁用 Query Cache ,因為他給數據庫設計帶來很多麻煩。 MySQL QC 造成的問題比他解決問題要多得多。 因此我們決定在 MySQL 8.0 中取消他,因為大家就不應該使用它。 如果你工作中需要使用 Query Cache,你應該用 ProxySQL as Query Cache 替代 Query Cache。
但是大多數情況下我會建議你不要使用查詢緩存,為什么呢?因為查詢緩存往往弊大於利。
查詢緩存的失效非常頻繁,只要有對一個表的更新,這個表上所有的查詢緩存都會被清空。因此很可能你費勁地把結果存起來,還沒使用呢,就被一個更新全清空了。對於更新壓力大的數據庫來說,查詢緩存的命中率會非常低。除非你的業務就是有一張靜態表,很長時間才會更新一次。比如,一個系統配置表,那這張表上的查詢才適合使用查詢緩存。
好在MySQL也提供了這種“按需使用”的方式。你可以將參數query_cache_type設置成DEMAND,這樣對於默認的SQL語句都不使用查詢緩存。而對於你確定要使用查詢緩存的語句,可以用SQL_CACHE顯式指定,像下面這個語句一樣:
mysql> select SQL_CACHE * from T where ID=10;
需要注意的是,MySQL 8.0版本直接將查詢緩存的整塊功能刪掉了,也就是說8.0開始徹底沒有這個功能了。
mysql5.7 mysql8都是默認關閉的
mysql> show variables like '%query_cache%' ; +------------------+-------+ | Variable_name | Value | +------------------+-------+ | have_query_cache | NO | +------------------+-------+
2.取消默認MyISAM系統表:並發程度低,資源利用率低。
3.移除PASSWORD()函數,無法再用SET PASSWORD=PASSWORD(密碼)去加密。