sql_safe_updates 是 MySQL 中的一個系統變量,用于控制 MySQL 服務器是否允許在沒有使用 KEY 或 LIMIT 子句的 UPDATE 或 DELETE 語句上執行更新或刪除操作。當這個變量被設置為 ON 時,MySQL 會拒絕那些可能影響到表中大量行的 UPDATE 或 DELETE 語句,除非這些語句明確使用了 WHERE 子句中的 KEY(如主鍵或唯一索引)或者 LIMIT 子句來限制影響的行數。
這樣做的目的是為了防止由于疏忽或錯誤編寫的 SQL 語句而導致大量數據的意外丟失或修改。
如何設置 sql_safe_updates
你可以通過幾種方式設置 sql_safe_updates:
全局級別:
你可以通過修改 MySQL 的配置文件(如 my.cnf 或 my.ini,取決于你的操作系統和 MySQL 版本)來永久設置這個變量。但是,請注意,直接在配置文件中設置 sql_safe_updates 可能不被所有 MySQL 版本支持,或者可能需要以不同的方式配置(如通過插件或其他系統變量)。
一種更常見的方法是使用 MySQL 的 SET GLOBAL 語句在運行時設置它,但這只會影響新的連接。例如:
SET GLOBAL sql_safe_updates = 1;
但是,請注意,直接設置全局變量可能需要管理員權限,并且這個更改不會影響已經存在的會話。
會話級別:
你可以通過在你的 MySQL 會話中執行以下 SQL 語句來設置 sql_safe_updates:
SET SESSION sql_safe_updates = 1;或者登錄時加上--safe-updates mysql -uroot -p --safe-updates
這會影響當前會話中的后續操作,但不會影響到其他會話或全局設置。
注意事項
- 在啟用 sql_safe_updates 后,如果你嘗試執行一個沒有 KEY 或 LIMIT 的 UPDATE 或 DELETE 語句,MySQL 將拒絕該操作并返回錯誤。
(root@localhost)[superdb]> show index from dept;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| dept | 0 | PRIMARY | 1 | deptno | A | 4 | NULL | NULL | | BTREE | | | YES | NULL |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
1 row in set (0.13 sec)(root@localhost)[superdb]> update dept set loc='sz';
ERROR 1175 (HY000): You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column.
-
并非所有 MySQL 部署都會默認啟用 sql_safe_updates。它通常是由數據庫管理員或開發者根據特定的安全要求來配置的。
-
在某些情況下,你可能需要臨時禁用 sql_safe_updates 以執行特定的批量更新或刪除操作。在這種情況下,你可以在會話級別設置 sql_safe_updates = 0,但請務必小心,確保你的 SQL 語句是安全的,不會意外地影響大量數據。
總之,sql_safe_updates 是一個有用的安全特性,可以幫助防止由于疏忽或錯誤導致的數據丟失。然而,它也要求開發者和數據庫管理員更加注意他們的 SQL 語句,以確保它們的安全性和準確性。
官方解釋
If set to 1, MySQL aborts UPDATE or DELETE statements that do not use a key in the WHERE clause or a LIMIT clause. (Specifically, UPDATE statements must have a WHERE clause that uses a key or a LIMIT clause, or both. DELETE statements must have both.) This makes it possible to catch UPDATE or DELETE statements where keys are not used properly and that would probably change or delete a large number of rows. The default value is 0.
當 sql_safe_updates 設置為 1 時。
- update 語句必須滿足如下條件之一才能執行成功:
- update 語句使用 where,并且 where 條件中必須有索引列;
- update 語句使用 limit;
- update 語句同時使用 where 和 limit,此時 where 條件中可以不帶有索引列;
(root@localhost)[superdb]> update dept set loc='sz' limit 1;
Query OK, 1 row affected (0.10 sec)
Rows matched: 1 Changed: 1 Warnings: 0(root@localhost)[superdb]> select * from dept;
+--------+------------+---------+
| deptno | dname | loc |
+--------+------------+---------+
| 10 | ACCOUNTING | sz |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+---------+
4 rows in set (0.00 sec)(root@localhost)[superdb]> update dept set loc='NEW YORK' limit 1;
Query OK, 1 row affected (0.05 sec)
Rows matched: 1 Changed: 1 Warnings: 0(root@localhost)[superdb]> update dept set loc='NEW YORK' where deptno=10;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1 Changed: 0 Warnings: 0(root@localhost)[superdb]> select * from dept;
+--------+------------+----------+
| deptno | dname | loc |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
4 rows in set (0.00 sec)(root@localhost)[superdb]> update dept set loc='NEW YORK' where deptno=10 limit 2;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1 Changed: 0 Warnings: 0(root@localhost)[superdb]> select * from dept;
+--------+------------+----------+
| deptno | dname | loc |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
4 rows in set (0.00 sec)
- delete 語句必須滿足以下條件能執行成功:
- delete 語句同時使用 where 條件中帶有索引列
- delete 語句同時使用 where 條件中帶有索引列 及 limit
- delete 語句同時使用 where 和 limit,此時 where 條件中可以不帶有索引列;
(root@localhost)[superdb]> insert into dept values(50,'sz','hk');
Query OK, 1 row affected (0.01 sec)-- 同時使用 where 和 limit,此時 where 條件中可以有索引列
(root@localhost)[superdb]> delete from dept where deptno=50 limit 1;
Query OK, 1 row affected (0.00 sec)(root@localhost)[superdb]> insert into dept values(50,'sz','hk');
Query OK, 1 row affected (0.00 sec)-- 僅使用 where條件中是索引列
(root@localhost)[superdb]> delete from dept where deptno=50;
Query OK, 1 row affected (0.01 sec)(root@localhost)[superdb]> insert into dept values(50,'sz','hk');
Query OK, 1 row affected (0.00 sec)-- dname不是索引列,因此無法刪除操作
(root@localhost)[superdb]> delete from dept where dname='sz';
ERROR 1175 (HY000): You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column. -- 同時使用 where 和 limit,此時 where 條件中沒有索引列
(root@localhost)[superdb]> delete from dept where dname='sz' limit 1;
Query OK, 1 row affected (0.05 sec)(root@localhost)[superdb]> select * from dept;
+--------+------------+----------+
| deptno | dname | loc |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
4 rows in set (0.00 sec)(root@localhost)[superdb]> show index from dept;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| dept | 0 | PRIMARY | 1 | deptno | A | 4 | NULL | NULL | | BTREE | | | YES | NULL |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
1 row in set (0.13 sec)
如果 where 條件帶上了索引列,但是優化器最終掃描選擇的是全表,而不是索引的話,我們可以使用 force index([index_name]) 可以告訴優化器使用哪個索引,以此避免有幾率鎖全表帶來的隱患。