MySQL的變量分類總結

 

在MySQL中,my.cnf是參數文件(Option Files),相似於ORACLE數據庫中的spfile、pfile參數文件,照理說,參數文件my.cnf中的都是系統參數(這種稱呼比較符合思惟習慣),可是官方又稱呼其爲系統變量(system variables),那麼到底這個叫系統參數或系統變量(system variables)呢? 這個曾經是一個讓我很糾結的問題,由於MySQL中有各類類型的變量,有時候語言就是這麼博大精深;相信不少人也對這個問題或多或少有點困惑。其實拋開這些名詞,它們就是同一個事情(東西),無論你叫它系統變量(system variables)或系統參數均可,無需那麼糾結。 就比如王三,有人叫他王三;也有人也叫他王麻子綽號同樣。html

 

另外,MySQL中有不少變量類型,確實有時候讓人有點混淆不清,本文打算總結一下MySQL數據庫的各類變量類型,理清各類變量類型概念。可以從全局有個清晰思路。MySQL變量類型具體參考下圖:mysql

 

clip_image001

 

 

 

 

Server System Variables(系統變量)sql

 

 

MySQL系統變量(system variables)是指MySQL實例的各類系統變量,其實是一些系統參數,用於初始化或設定數據庫對系統資源的佔用,文件存放位置等等,這些變量包含MySQL編譯時的參數默認值,或者my.cnf配置文件裏配置的參數值。默認狀況下系統變量都是小寫字母。官方文檔介紹以下:數據庫

 

The MySQL server maintains many system variables that indicate how it is configured. Each system variable has a default value. System variables can be set at server startup using options on the command line or in an option file. Most of them can be changed dynamically at runtime using the SET statement, which enables you to modify operation of the server without having to stop and restart it. You can also use system variable values in expressions.express

 

 

系統變量(system variables)按做用域範圍能夠分爲會話級別系統變量和全局級別系統變量。若是要確認系統變量是全局級別仍是會話級別,能夠參考官方文檔,若是Scope其值爲GLOBAL或SESSION,表示變量既是全局級別系統變量,又是會話級別系統變量。若是其Scope其值爲GLOBAL,表示系統變量爲全局級別系統變量。c#

 

--查看系統變量的全局值服務器

 

select * from information_schema.global_variables;session

select * from information_schema.global_variablesapp

  where variable_name='xxxx';ide

select * from performance_schema.global_variables;

 

 

--查看系統變量的當前會話值

 

select * from information_schema.session_variables;

    select * from information_schema.session_variables

  where variable_name='xxxx';

select * from performance_schema.session_variables;

 

 

 

 

SELECT @@global.sql_mode, @@session.sql_mode, @@sql_mode;

 

mysql> show variables like '%connect_timeout%'; 

mysql> show local variables like '%connect_timeout%';

mysql> show session variables like '%connect_timeout%';

mysql> show global variables like '%connect_timeout%';

 

注意:對於SHOW VARIABLES,若是不指定GLOBAL、SESSION或者LOCAL,MySQL返回SESSION值,若是要區分系統變量是全局仍是會話級別。不能使用下面方式,若是某一個系統變量是全局級別的,那麼在當前會話的值也是全局級別的值。例如系統變量AUTOMATIC_SP_PRIVILEGES,它是一個全局級別系統變量,可是 show session variables like '%automatic_sp_privileges%'同樣能查到其值。因此這種方式沒法區別系統變量是會話級別仍是全局級別。

 

mysql> show session variables like '%automatic_sp_privileges%';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| automatic_sp_privileges | ON    |
+-------------------------+-------+
1 row in set (0.00 sec)
 
mysql> select * from information_schema.global_variables
    -> where variable_name='automatic_sp_privileges';
+-------------------------+----------------+
| VARIABLE_NAME           | VARIABLE_VALUE |
+-------------------------+----------------+
| AUTOMATIC_SP_PRIVILEGES | ON             |
+-------------------------+----------------+
1 row in set, 1 warning (0.00 sec)
 
mysql> 

 

 

若是要區分系統變量是全局仍是會話級別,能夠用下面方式:

 

方法1: 查官方文檔中系統變量的Scope屬性。

方法2: 使用SET VARIABLE_NAME=xxx; 若是報ERROR 1229 (HY000),則表示該變量爲全局,若是不報錯,那麼證實該系統變量爲全局和會話兩個級別。

   

 

mysql> SET AUTOMATIC_SP_PRIVILEGES=OFF;
 
ERROR 1229 (HY000): Variable 'automatic_sp_privileges' is a GLOBAL variable and should be set with SET GLOBAL

 

 

 

可使用SET命令修改系統變量的值,以下所示:

 

修改全局級別系統變量:

 

SET GLOBAL max_connections=300;
 
SET @@global.max_connections=300;

 

注意:更改全局變量的值,須要擁有SUPER權限

 

修改會話級別系統變量:

 

   SET @@session.max_join_size=DEFAULT;

  SET max_join_size=DEFAULT;  --默認爲會話變量。若是在變量名前沒有級別限定符,表示修改會話級變量。

   SET SESSION max_join_size=DEFAULT;

 

若是修改系統全局變量沒有指定GLOBAL或@@global的話,就會報Variable 'xxx' is a GLOBAL variable and should be set with SET GLOBAL這類錯誤。

 

mysql> set max_connections=300;
ERROR 1229 (HY000): Variable 'max_connections' is a GLOBAL variable and should be set with SET GLOBAL
mysql> set global max_connections=300;
Query OK, 0 rows affected (0.00 sec)
 
mysql> 

 

 

 

系統變量(system variables)按是否能夠動態修改,能夠分爲系統動態變量(Dynamic System Variables)和系統靜態變量。怎麼區分系統變量是動態和靜態的呢? 這個只能查看官方文檔,系統變量的"Dynamic"屬性爲Yes,則表示能夠動態修改。Dynamic Variable具體能夠參考https://dev.mysql.com/doc/refman/5.7/en/dynamic-system-variables.html

 

另外,有些系統變量是隻讀的,不能修改的。以下所示:

 

mysql>

mysql> set global innodb_version='5.6.21';

ERROR 1238 (HY000): Variable 'innodb_version' is a read only variable

mysql>

 

 

另外,還有一個Structured System Variables概念,其實就是系統變量是一個結構體(Strut),官方介紹以下所示:

 

Structured System Variables

 

A structured variable differs from a regular system variable in two respects:

 

Its value is a structure with components that specify server parameters considered to be closely related.

 

There might be several instances of a given type of structured variable. Each one has a different name and refers to a different resource maintained by the server.

 

 

 

 

Server Status Variables(服務器狀態變量)

 

 

MySQL狀態變量(Server Status Variables)是當前服務器從啓動後累計的一些系統狀態信息,例如最大鏈接數,累計的中斷鏈接等等,主要用於評估當前系統資源的使用狀況以進一步分析系統性能而作出相應的調整決策。這個估計有人會跟系統變量混淆,其實狀態變量是動態變化的,另外,狀態變量是隻讀的:只能由MySQL服務器自己設置和修改,對於用戶來講是隻讀的,不能夠經過SET語句設置和修改它們,而系統變量則能夠隨時修改。狀態變量也分爲會話級與全局級別狀態信息。有些狀態變量能夠用FLUSH STATUS語句重置爲零值。

 

關於查看狀態變量,show status也支持like匹配查詢。以下所示:

 

show status like '%variable_name%'

show global status like '%variable_name%'

 

  

#當前測試環境
ysql> select version() from dual;
-----------+
 version() |
-----------+
 5.7.21    |
-----------+
 row in set (0.00 sec)
   
mysql> show status;  --查看全部的狀態變量
 
 
 
ysql> show global status like 'Aborted_connects%';
------------------+-------+
 Variable_name    | Value |
------------------+-------+
 Aborted_connects | 2     |
------------------+-------+
 row in set (0.01 sec)
 
ysql> show session status like 'Aborted_connects%';
------------------+-------+
 Variable_name    | Value |
------------------+-------+
 Aborted_connects | 2     |
------------------+-------+
 row in set (0.00 sec)
 
ysql> select * from information_schema.global_status;
RROR 3167 (HY000): The 'INFORMATION_SCHEMA.GLOBAL_STATUS' feature is disabled; see the documentation for 'show_compatibility_56'
ysql> #
ysql> show variables like '%show_compatibility_56%';
-----------------------+-------+
 Variable_name         | Value |
-----------------------+-------+
 show_compatibility_56 | OFF   |
-----------------------+-------+
 row in set (0.00 sec)
 
ysql> set global show_compatibility_56=on;
uery OK, 0 rows affected (0.00 sec)
 
ysql> select * from information_schema.global_status;
-----------------------------------------------+---------------------------------------+
 VARIABLE_NAME                                  VARIABLE_VALUE                         |
-----------------------------------------------+---------------------------------------+
 ABORTED_CLIENTS                               | 138097                                |
 ABORTED_CONNECTS                              | 5                                     |
 BINLOG_CACHE_DISK_USE                         | 0                                     |
 BINLOG_CACHE_USE                              | 0                                     |
....................................................................................
 
 
 
select * from performance_schema.global_status;
select * from performance_schema.session_status;

 

 

注意:MySQL 5.7之後系統變量和狀態變量須要從performance_schema中進行獲取,information_schema仍然保留了GLOBAL_STATUS,GLOBAL_VARIABLES兩個表作兼容,若是但願沿用information_schema中進行查詢的習慣,5.7提供了show_compatibility_56參數,設置爲ON能夠兼容5.7以前的用法,不然就會報錯(ERROR 3167 (HY000)).

 

 

 

 

User-Defined Variables(用戶自定義變量)

 

 

用戶自定義變量,顧名思義就是用戶本身定義的變量。用戶自定義變量是基於當前會話的。 也就是說用戶自定義變量的做用域侷限於當前會話(鏈接),由一個客戶端定義的用戶自定義變量不能被其餘客戶端看到或使用。(例外:能夠訪問performance_schema.user_variables_by_thread表的用戶能夠看到全部會話的定義的用戶自定義變量,固然僅僅能看到那些會話定義了哪些變量,而不能訪問這些變量。)。當客戶端會話退出時,當前會話全部的自定義變量都會自動釋放。

通常能夠在SQL語句將值存儲在用戶自定義變量中,而後再利用另外一條SQL語句來查詢用戶自定義變量。這樣以來,能夠在不一樣的SQL間傳遞值。

 

另外,用戶自定義變量是大小寫不敏感的,最大長度爲64個字符,用戶自定義變量的形式通常爲@var_name,其中變量名稱由字母、數字、._$組成。固然,在以字符串或者標識符引用時也能夠包含其餘特殊字符(例如:@'my-var',@"my-var",或者@`my-var`)。。使用SET設置變量時,可使用=或者:=操做符進行賦值。對於SET,可使用=或:=來賦值,對於SELECT只能使用:=來賦值。以下所示:

 

   

mysql> set @$test1="test";
Query OK, 0 rows affected (0.00 sec)
mysql> select @$test1 from dual;
+---------+
| @$test1 |
+---------+
| test    |
+---------+
1 row in set (0.00 sec)
 
mysql> 
mysql> set @"ac#k":='kerry';
Query OK, 0 rows affected (0.00 sec)
 
mysql> select @"ac#k" from dual;
+---------+
| @"ac#k" |
+---------+
| kerry   |
+---------+
1 row in set (0.00 sec)
 
mysql> 
 
 
mysql> select version() from dual;
+-----------+
| version() |
+-----------+
| 5.7.21    |
+-----------+
1 row in set (0.00 sec)
 
mysql> 
mysql> set @my_test=1200;
Query OK, 0 rows affected (0.00 sec)
 
mysql> select @my_test;
+----------+
| @my_test |
+----------+
|     1200 |
+----------+
1 row in set (0.00 sec)
 
mysql> select connection_id() from dual;
+-----------------+
| connection_id() |
+-----------------+
|          149379 |
+-----------------+
1 row in set (0.00 sec)
 
mysql> SELECT c.id, 
    ->        b.thread_id
    -> FROM   performance_schema.threads b 
    ->     join information_schema.processlist c 
    ->          ON b.processlist_id = c.id 
    -> where c.id=149379;
+--------+-----------+
| id     | thread_id |
+--------+-----------+
| 149379 |    149404 |
+--------+-----------+
1 row in set (0.00 sec)
 
mysql> select @My_Test, @my_TEST from dual;
+----------+----------+
| @My_Test | @my_TEST |
+----------+----------+
|     1200 |     1200 |
+----------+----------+
1 row in set (0.00 sec)
 
mysql> 

 

 

 clip_image002

 

 

mysql> select connection_id() from dual;
+-----------------+
| connection_id() |
+-----------------+
|          151821 |
+-----------------+
1 row in set (0.00 sec)
 
mysql> select @my_test from dual;
+----------+
| @my_test |
+----------+
| NULL     |
+----------+
1 row in set (0.00 sec)
 
mysql> select * from performance_schema.user_variables_by_thread;
+-----------+---------------+----------------+
| THREAD_ID | VARIABLE_NAME | VARIABLE_VALUE |
+-----------+---------------+----------------+
|    149404 | my_test       | 1200           |
+-----------+---------------+----------------+
1 row in set (0.00 sec)
 
mysql> 

 

 

clip_image003

 

 

 

用戶自定義變量注意事項,如下爲總結:

 

1:未定義的用戶自定義變量初始值是NULL

 

mysql> select @kerry from dual;
 
+--------+
 
| @kerry |
 
+--------+
 
| NULL   |
 
+--------+
 
1 row in set (0.00 sec)

 

注意:使用未定義變量不會產生任何語法錯誤,因爲其被初始化爲NULL值,若是沒有意識到這一點,很是容易犯錯。以下所示:

 

mysql> select @num1, @num2 :=@num1+1 from dual;
+-------+-----------------+
| @num1 | @num2 :=@num1+1 |
+-------+-----------------+
| NULL  |            NULL |
+-------+-----------------+
1 row in set (0.00 sec)
 
mysql>

 

 

 

2:用戶變量名對大小寫不敏感(上面已經敘述,此處從略)

 

3:自定義變量的類型是一個動態類型。

 

MySQL中用戶自定義變量,不嚴格限制數據類型的,它的數據類型根據你賦給它的值而隨時變化。並且自定義變量若是賦予數字值,是不能保證進度的。官方文檔介紹:

 

User variables can be assigned a value from a limited set of data types: integer, decimal, floating-point, binary or nonbinary string, or NULL value. Assignment of decimal and real values does not preserve the precision or scale of the value. A value of a type other than one of the permissible types is converted to a permissible type. For example, a value having a temporal or spatial data type is converted to a binary string. A value having the JSON data type is converted to a string with a character set of utf8mb4 and a collation of utf8mb4_bin.

 

 

 

4:賦值的順序和賦值的時間點並不老是固定的,這依賴於優化器的決定。

 

 

使用用戶自定義變量的一個最多見的問題就是沒有注意到在賦值和讀取用戶自定義變量的時候多是在查詢的不一樣階段。例如,在SELECT語句中進行賦值而後再WHERE子句中讀取用戶自定義變量,則可能用戶自定義變量取值並不不是你所想象的那樣,以下例子所示,由於按照MySQL語句的執行順序,WHERE部分優先與SELECT部分操做,因此你會看到msgid 和 @rownum的最大值爲6.

 

mysql> select msgid from message order by msgid limit 12;
+-------+
| msgid |
+-------+
|     1 |
|     2 |
|     3 |
|     4 |
|     5 |
|     6 |
|     7 |
|    11 |
|    12 |
|    13 |
|    18 |
|    19 |
+-------+
12 rows in set (0.00 sec)
 
mysql> set @rownum := 0;
Query OK, 0 rows affected (0.00 sec)
 
mysql> select msgid , @rownum := @rownum +1 as rownum
    -> from message
    -> where @rownum <=5;
+-------+--------+
| msgid | rownum |
+-------+--------+
|     1 |      1 |
|     2 |      2 |
|     3 |      3 |
|     4 |      4 |
|     5 |      5 |
|     6 |      6 |
+-------+--------+
6 rows in set (0.00 sec)
 
mysql> select msgid , @rownum := @rownum +1 as rownum
    -> from message
    -> where @rownum <=5;
Empty set (0.00 sec)
 
mysql> select @rownum from dual;
+---------+
| @rownum |
+---------+
|       6 |
+---------+
1 row in set (0.00 sec)
 
mysql> 

 

clip_image004

 

如上所示,第二次查詢可能你想要的邏輯跟實際邏輯已經出現了誤差,這個是使用自定義變量須要當心的地方。由於用戶自定義變量在當前會話中也算一個全局變量,它已經變成了6,where條件後面的 @rownum <= 5 邏輯爲false了。一不小當心就會出現和你預想的結果出現誤差。

 

 

不要在同一個非SET語句中同時賦值並使用同一個用戶自定義變量,由於WHERE和SELECT是在查詢執行的不一樣階段被執行的。若是在查詢中再加入ORDER BY的話,結果可能會更不一樣;

 

mysql> set @rownum :=0;
Query OK, 0 rows affected (0.00 sec)
 
mysql> select msgid , @rownum := @rownum +1 as rownum
    -> from message
    -> where @rownum <=5;
+-------+--------+
| msgid | rownum |
+-------+--------+
|     1 |      1 |
|     2 |      2 |
|     3 |      3 |
|     4 |      4 |
|     5 |      5 |
|     6 |      6 |
+-------+--------+
6 rows in set (0.00 sec)
 
mysql> 
 
 
 
mysql> set @rownum := 0;
Query OK, 0 rows affected (0.00 sec)
 
mysql> select msgid, @rownum := @rownum +1 as rownum
    -> from message
    -> where @rownum <=5
    -> order by msgcontent;
+-------+--------+
| msgid | rownum |
+-------+--------+
|    20 |      1 |
|    28 |      2 |
|    43 |      3 |
|    47 |      4 |
..................
..................
|    22 |     57 |
|    69 |     58 |
|    40 |     59 |
|    52 |     60 |
|    24 |     61 |
|    66 |     62 |
|    51 |     63 |
+-------+--------+
63 rows in set (0.00 sec)
 
mysql> 

 

clip_image005

 

 

若是按msgid排序,那麼又是正常的,那三者有啥區別呢?

 

mysql> set @rownum :=0;
Query OK, 0 rows affected (0.00 sec)
 
mysql> select msgid, @rownum := @rownum +1 as rownum
    -> from message
    -> where @rownum <=5
    -> order by msgid;
+-------+--------+
| msgid | rownum |
+-------+--------+
|     1 |      1 |
|     2 |      2 |
|     3 |      3 |
|     4 |      4 |
|     5 |      5 |
|     6 |      6 |
+-------+--------+
6 rows in set (0.00 sec)
 
mysql> 

 

咱們先看執行計劃

 

image

 

官方的解釋以下:

 

In a SELECT statement, each select expression is evaluated only when sent to the client. This means that in a HAVING, GROUP BY, or ORDER BY clause, referring to a variable that is assigned a value in the select expression list does not work as expected

 

在SELECT語句中,每一個選擇表達式僅在發送給客戶端時才被計算。 這意味着在HAVING,GROUP BY或ORDER BY子句中,引用在選擇表達式列表中指定值的用戶自定義變量不能按預期工做。 也就是說用戶自定義變量的值是在結果集發送到客戶端後才計算的

 

測試官方的例子:

 

clip_image006

 

 

clip_image007

 

 

這種解釋算是比較權威的,可是,讓人有點不解的是,SQL執行順序中WHERE在SELECT操做以前, 可是第一個SQL語句又怎麼解釋呢?有種解釋是MySQL優化器在某些場景下可能會將這些變量優化掉,這可能致使代碼不按預想的方式運行。 解決這個問題的辦法是讓變量的賦值和取值發生在執行查詢的同一階段,以下所示:

 

clip_image008

 

 

 

 

關於用戶自定義變量,若是運用的好,可以寫出高效簡潔的SQL語句,若是運用不當,也可能把本身給坑了。這個徹底取決於使用它的人。

 

官方文檔也有介紹用戶自定義變量不適合使用場景。摘抄部分以下:

 

User variables may be used in most contexts where expressions are permitted. This does not currently include contexts that explicitly require a literal value, such as in the LIMIT clause of a SELECT statement, or the IGNORE N LINES clause of a LOAD DATA statement.

 

User variables are intended to provide data values. They cannot be used directly in an SQL statement as an identifier or as part of an identifier, such as in contexts where a table or database name is expected, or as a reserved word such as SELECT.

 

 

 

局部變量

 

 

 

局部變量:做用範圍在begin到end語句塊之間。在該語句塊裏設置的變量。declare語句專門用於定義聲明局部變量。

 

 

局部變量與用戶自定義變量的區分在於下面這些方面:

 

1.用戶自定義變量是以"@"開頭的。局部變量沒有這個符號。

 

2.定義變量方式不一樣。用戶自定義變量使用set語句,局部變量使用declare語句定義

 

3.做用範圍不一樣。局部變量只在begin-end語句塊之間有效。在begin-end語句塊運行完以後,局部變量就消失了。而用戶自定義變量是對當前鏈接(會話)有效。

 

 

 

 

 

 

 

參考資料:

 

https://dev.mysql.com/doc/refman/5.7/en/user-variables.html

https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html

https://dev.mysql.com/doc/refman/5.7/en/using-system-variables.html

https://dev.mysql.com/doc/refman/5.7/en/structured-system-variables.html

https://dev.mysql.com/doc/refman/5.7/en/declare-local-variable.html

https://www.jianshu.com/p/357a02fb2d64

相關文章
相關標籤/搜索