MySQL 擁有四層變量體系:用戶變量、系統變量(會話/全局)、局部變量。混淆它們是導致「變量不生效、作用域錯亂、權限報錯」的高頻原因。本文從定義語法、作用域、生命周期、典型坑點四個維度建立清晰認知模型。
1. 四類變量對比總覽
| 類別 | 標識 | 定義語法 | 作用域 | 生命周期 | 權限要求 |
| 用戶變量 | @var | SET @v=1; / SELECT @v:=1; | 當前會話 | 會話結束消失 | 無 |
| 系統會話變量 | @@session.var / @@var | SET SESSION var=val; | 當前會話 | 會話結束重置 | 無 |
| 系統全局變量 | @@global.var | SET GLOBAL var=val; | 所有會話 | 重啟失效(需寫入 cnf 持久化) | SUPER 權限 |
| 局部變量 | 無前綴 | DECLARE var TYPE [DEFAULT val]; | BEGIN...END 塊內 | 塊結束銷毀 | 無 |
2. 核心區別深度講解
用戶變量 vs 系統變量
- 所有權不同:用戶變量由用戶自定義(名稱、類型、值);系統變量由 MySQL 內建定義(名稱、類型、默認值、合法範圍),用戶只能**修改值**,不能創建新系統變量。
- 語法差異:
- 用戶變量:
SET @a=1; 或 SELECT @a:=1;(:= 為賦值,= 在 SELECT 中為比較)
- 系統變量:
SET SESSION sort_buffer_size=1M; / SET GLOBAL max_connections=500; / SET @@session.sort_buffer_size=1M;
- 持久化:系統全局變量修改僅運行時生效,重啟失效;持久化需寫入
/etc/my.cnf [mysqld] 區段。
局部變量的特殊性
- **僅能在存儲過程/函數/觸發器的
BEGIN...END 塊首部聲明**(DECLARE 必須在任何其他語句之前)。
- 無
@ / @@ 前綴,賦值用 SET var=val;(同會話系統變量語法,靠上下文區分)。
- 塊結束即銷毀,**無法跨塊、跨過程訪問**,若需傳值需用 OUT 參數或寫入用戶變量。
會話 vs 全局系統變量的遞關係
- 新會話啟動時,**複製**全局變量值作為會話變量初始值。
- 會話變量修改**不影響**全局變量,也不影響其他會話。
SET var=val; 不指定作用域時,**默認修改會話變量**;SET @@var=val; 同理。
3. 典型坑點與避雷指南
| 現象 | 根因 | 正確做法 |
SET @x=1; 後新連接 SELECT @x 為 NULL | 用戶變量綁定會話,跨會話不可見 | 跨會話傳值用表、Redis 或持久化配置 |
存儲過程中 SET sort_buffer_size=1M; 報錯 | 局部變量聲明區未結束,被誤認為聲明局部變量 | 用 SET SESSION sort_buffer_size=1M; 顯式指定 |
SET GLOBAL max_connections=1000; 重啟失效 | 運行時修改不持久化 | 同步修改 /etc/my.cnf max_connections=1000 |
SELECT @a:=SUM(x) FROM t; 結果為 NULL | 未初始化用戶變量隱式為 NULL,聚合結果賦值給 NULL | 先 SET @a=0; 或用 SELECT SUM(x) INTO @a FROM t; |
4. 實戰最佳實踐
- 應用層配置參數:啟動腳本執行
SET SESSION ... 或連接池初始化 SQL 設置會話變量(如 sql_mode、time_zone)。
- 存儲過程參數化:優先用 IN/OUT/INOUT 參數傳值,少用用戶變量跨過程通信(耦合高、難調試)。
- 全局變量變更流程:
SET GLOBAL 立即生效 → 同步更新 my.cnf → 變更記錄入庫審計。
- 命名規範:用戶變量
@biz_*、局部變量 v_*、系統變量保持原名,提升可讀性。
5. 快速驗證命令
SHOW VARIABLES LIKE 'max_connections'; -- 看系統變量當前值
SHOW SESSION VARIABLES LIKE 'sort_buffer%'; -- 看會話級
SHOW GLOBAL VARIABLES LIKE 'max_conn%'; -- 看全局級
SELECT @my_var; -- 看用戶變量