Mysql 限制无条件更新或删除数据(sql_safe_updates)

哎,总是会有些误操作或代码bug(特别是动态SQL),会执行一些无条件(或无有效条件 where 1=1 )更新或删除操作,让人防不胜防,特别是在大表上发生这样的事故的时候,搞得人好心累。

mysql有一个动态变量(sql_safe_updates),可以有效避免(不能完成避免)这种情况的发生。

 

下面将对此变量进行一些测试:mysql5.7

# 进行测试数据准备
mysql> drop table IF EXISTS test_safe;
Query OK, 0 rows affected (0.01 sec)

mysql> CREATE table IF NOT EXISTS  test_safe(
    -> id int auto_increment primary key ,    #主键列
    -> vname  varchar(32) ,
    -> age tinyint default 0,
    -> create_time datetime default now(),
    -> key idx_vname(vname)      #给vname列添加索引
    -> );
insert into test_safe(vname,age) values("AAA",1),("BBB",2),("CCC",3),("DDD",4),("EEE",5),("FFF",6),("GGG",7),("HHH",8),("III",9),("JJJ",10);
Query OK, 0 rows affected (0.08 sec)

mysql> insert into test_safe(vname,age) values("AAA",1),("BBB",2),("CCC",3),("DDD",4),("EEE",5),("FFF",6),("GGG",7),("HHH",8),("III",9),("JJJ",10);
Query OK, 10 rows affected (0.00 sec)
Records: 10  Duplicates: 0  Warnings: 0

mysql> desc test_safe;
+-------------+-------------+------+-----+-------------------+----------------+
| Field       | Type        | Null | Key | Default           | Extra          |
+-------------+-------------+------+-----+-------------------+----------------+
| id          | int(11)     | NO   | PRI | NULL              | auto_increment |
| vname       | varchar(32) | YES  | MUL | NULL              |                |
| age         | tinyint(4)  | YES  |     | 0                 |                |
| create_time | datetime    | YES  |     | CURRENT_TIMESTAMP |                |
+-------------+-------------+------+-----+-------------------+----------------+
4 rows in set (0.01 sec)

mysql> select * from test_safe;
+----+-------+------+---------------------+
| id | vname | age  | create_time         |
+----+-------+------+---------------------+
|  1 | AAA   |    1 | 2020-11-13 13:38:37 |
|  2 | BBB   |    2 | 2020-11-13 13:38:37 |
|  3 | CCC   |    3 | 2020-11-13 13:38:37 |
|  4 | DDD   |    4 | 2020-11-13 13:38:37 |
|  5 | EEE   |    5 | 2020-11-13 13:38:37 |
|  6 | FFF   |    6 | 2020-11-13 13:38:37 |
|  7 | GGG   |    7 | 2020-11-13 13:38:37 |
|  8 | HHH   |    8 | 2020-11-13 13:38:37 |
|  9 | III   |    9 | 2020-11-13 13:38:37 |
| 10 | JJJ   |   10 | 2020-11-13 13:38:37 |
+----+-------+------+---------------------+
10 rows in set (0.00 sec)






#在全局开启安全更新模式
mysql> set global sql_safe_updates=1;
Query OK, 0 rows affected (0.00 sec)

#在当前会话开启安全更新模式
mysql> set sql_safe_updates=1;
Query OK, 0 rows affected (0.00 sec)

mysql> show global variables like 'sql_safe_updates';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| sql_safe_updates | ON    |
+------------------+-------+
1 row in set (0.01 sec)

mysql> show variables like 'sql_safe_updates';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| sql_safe_updates | ON    |
+------------------+-------+
1 row in set (0.01 sec)







#测试1:无条件更新。(执行失败)
mysql> UPDATE test_safe SET age = age+1;
ERROR 1175 (HY000): You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column. 

#测试2:WHERE 1=1 更新。(执行失败)
mysql> UPDATE test_safe SET age = age+1 WHERE 1=1;
ERROR 1175 (HY000): You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column. 

#测试3:根据非索引列条件更新。(执行失败)
mysql> UPDATE test_safe SET age = age+1 WHERE create_time<NOW();
ERROR 1175 (HY000): You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column. 

#测试4:根据索引列条件更新。(执行成功)
mysql> UPDATE test_safe SET age = age+1 WHERE vname>'000';
Query OK, 10 rows affected (0.01 sec)
Rows matched: 10  Changed: 10  Warnings: 0

#测试5:limit 更新。(执行成功)
mysql> UPDATE test_safe SET age = age+1 WHERE 1=1 LIMIT 10;
Query OK, 10 rows affected (0.01 sec)
Rows matched: 10  Changed: 10  Warnings: 0


mysql> UPDATE test_safe SET age = age+1 LIMIT 10;
Query OK, 10 rows affected (0.00 sec)
Rows matched: 10  Changed: 10  Warnings: 0

#测试5:like '%XX' 更新。(执行失败)
mysql> UPDATE test_safe SET age = age+1 WHERE vname LIKE '%000';
ERROR 1175 (HY000): You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column. 

#测试6:like 'XX%' 更新。(执行成功)
mysql> UPDATE test_safe SET age = age+1 WHERE vname LIKE '000%';
Query OK, 0 rows affected (0.02 sec)
Rows matched: 0  Changed: 0  Warnings: 0


#测试7:无条件删除。(执行失败)
mysql> DELETE FROM test_safe ;
ERROR 1175 (HY000): You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column. 

#测试8:where 1=1 删除。(执行失败)
mysql> DELETE FROM test_safe WHERE 1=1;
ERROR 1175 (HY000): You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column. 

#测试9:根据非索引列条件删除。(执行失败)
mysql> DELETE FROM  test_safe WHERE create_time<NOW();
ERROR 1175 (HY000): You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column. 


#测试10:limit 删除。(执行成功)
mysql> DELETE FROM  test_safe LIMIT 1;
Query OK, 1 row affected (0.00 sec)

#测试11:根据索引列进行删除。(执行成功)
mysql> DELETE FROM test_safe WHERE vname='CCC';
Query OK, 1 row affected (0.00 sec)

#测试12:根据索引列进行删除。(执行成功)
mysql> DELETE FROM test_safe WHERE vname <> 'CCC';
Query OK, 8 rows affected (0.01 sec)

开启安全更新模式后,update或delete语句必须满足如下条件之一才能执行成功:

1.使用了limit条件

2.where条件中包含索引列,且可以使用到索引。(这个地方不好表述,建议看上面的测试例子进行意会)

3.where条件中不包含索引列,但是使用了limit。

 

虽然开启安全更新可以避免无条件(where 1=1等)的更新(或删除)操作,但是也不能完成避免全表更新(或删除)操作,像测试4、测试9、测试12中,使用到了索引列,但是给出的条件表中所有的行都满足条件,一样实现了全表更新(或删除)。

 

注意 : 阿里云 RDS(mysql5.7)不支持此参数,8.0的不知道,没有用过。

 

个人建议

1.尽量不要使用动态SQL,太复杂的情况判断下,开发者都不知道会生成什么样的SQL。

2.根据主键或索引列进行 等值 的更新或删除操作。

3.加上limit  (然后在实际中这个值并不好控制,尴尬)



 

<think>好的,我现在要解决用户的问题:如何防止修复MySQL数据被自动删除的问题。首先,我需要理解用户的具体情况。用户提到MySQL自动删除数据,可能的原因有多种,比如误操作、配置问题、触发器事件、主键一致等。根据用户提供的引用内容,有几个关键点需要注意: 首先,引用[1]提到了sql_safe_updates=on时,如果没有WHERE条件者使用索引字段,删除操作会被阻止。这可能和用户的情况有关,如果用户的数据被自动删除,可能是因为某些操作绕过了安全模式,者有事件触发了删除。 引用[3]提到主键被占用后即使数据删除,主键依然可用,导致主键连续。这可能和用户的问题关联大,但需要确认是否有主键问题导致数据异常。 接下来,我需要考虑可能的解决方案。根据常见的MySQL问题,数据被自动删除的可能原因包括: 1. 误操作:用户管理员小心执行了DELETE语句。 2. 自动清理机制:如设置了定时任务事件自动删除数据。 3. 触发器:某个表上的触发器在特定条件下自动删除数据。 4. 配置问题:比如某些配置导致数据被意外清理,比如binlog过期时间过短。 5. 外键约束:ON DELETE CASCADE导致级联删除。 6. 主从同步问题:从库执行了误操作。 针对这些可能性,我需要逐步排查。 首先,检查是否有自动删除的事件任务。查看MySQL的事件调度器是否启用,并检查相关事件。用户可能需要检查information_schema.events表,查找是否有定时执行的DELETE语句。 其次,检查触发器。查看是否有触发器在特定操作后删除数据,可以使用SHOW TRIGGERS命令。 第三,检查外键约束。如果表之间有外键关系,并且设置了ON DELETE CASCADE,删除父表数据可能导致子表数据删除。需要检查表结构中的外键设置。 第四,检查binlog和日志,确定删除操作的时间点和具体SQL语句。通过分析binloggeneral log,可以找到执行删除的源头。 第五,确认是否启用了安全更新模式。如果sql_safe_updates=on,可能阻止无WHERE的删除,但如果关闭了,可能导致误操作。 根据引用[2]提到的数据恢复方法,如果数据已经被删除,可能需要从备份binlog中恢复。但用户的问题更多是防止再次发生,所以预防措施更重要。 用户需要采取的步骤可能包括:启用安全模式、检查事件和触发器、审查外键约束、定期备份、监控日志等。同时,设置适当的权限,避免必要的删除权限,也是重要的。 现在需要将这些思路整理成结构化的回答,分步骤说明原因和解决方法,并引用提供的资料中的相关信息。例如,引用[1]的安全模式设置,引用[3]的主键问题可能直接相关,但需注意。 最后,生成相关问题,帮助用户进一步理解可能涉及的方面,比如如何恢复数据、如何设置安全模式等。</think>### 解决MySQL数据被自动删除问题的方法 #### 1. **确认数据删除原因** - **检查自动删除事件** 通过查询`information_schema.events`表,检查是否有定时任务自动执行`DELETE`操作: ```sql SELECT EVENT_NAME, EVENT_DEFINITION, INTERVAL_VALUE, INTERVAL_FIELD FROM information_schema.EVENTS WHERE EVENT_DEFINITION LIKE '%DELETE%'; ``` 如果存在相关事件,可通过`DROP EVENT`命令删除。 - **排查触发器** 使用`SHOW TRIGGERS`查看所有触发器,确认是否有触发器在特定操作(如`INSERT`、`UPDATE`)后触发删除: ```sql SHOW TRIGGERS FROM your_database_name; ``` 若存在可疑触发器,使用`DROP TRIGGER`删除。 - **外键约束导致的级联删除** 检查表结构中的外键是否包含`ON DELETE CASCADE`: ```sql SELECT TABLE_NAME, COLUMN_NAME, CONSTRAINT_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME, DELETE_RULE FROM information_schema.KEY_COLUMN_USAGE WHERE REFERENCED_TABLE_NAME IS NOT NULL AND DELETE_RULE = 'CASCADE'; ``` 若需禁用级联删除,需修改外键约束为`ON DELETE NO ACTION`。 --- #### 2. **启用安全防护机制** - **开启安全更新模式** 设置`sql_safe_updates=ON`,禁止无`WHERE`条件未使用索引的`DELETE`/`UPDATE`操作[^1]: ```sql SET sql_safe_updates = 1; ``` 在配置文件`my.cnf`中添加以下配置以永久生效: ```ini [mysqld] sql_safe_updates = ON ``` - **限制用户权限** 为应用程序账户分配最小权限,避免授予全局`DELETE`权限: ```sql GRANT SELECT, INSERT, UPDATE ON your_database.* TO 'app_user'@'%'; ``` --- #### 3. **数据恢复与备份** - **从备份恢复数据** 若有定期备份(如通过`mysqldump`物理备份工具),直接还原备份文件: ```bash mysql -u root -p your_database < backup.sql ``` - **通过Binlog恢复** 若未备份但开启了二进制日志,可通过`mysqlbinlog`工具解析并重放日志: ```bash mysqlbinlog --start-datetime="2024-01-01 00:00:00" --stop-datetime="2024-01-02 00:00:00" mysql-bin.000001 | mysql -u root -p ``` 需根据删除操作发生时间定位具体日志文件[^2]。 --- #### 4. **监控与日志分析** - **启用General Log** 临时开启通用查询日志,记录所有SQL语句: ```sql SET GLOBAL general_log = 'ON'; SET GLOBAL log_output = 'TABLE'; ``` 分析`mysql.general_log`表,查找可疑的`DELETE`语句。 - **审计插件(Enterprise版)** 若使用MySQL企业版,可通过审计插件记录高危操作。 --- #### 5. **避免主键冲突导致的数据异常** 若因主键连续导致数据插入异常(如自增主键跳跃),可重置自增值: ```sql ALTER TABLE your_table AUTO_INCREMENT = 1; ``` 但需注意这可能与现有数据冲突[^3]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值