暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL 5.7&8.0开启sql_safe_updates安全模式的差异

DBA的辛酸事儿 2021-06-29
1341

不知道大家是否有过维护的数据库表业务数据被人或者因为程序bug导致全表更新,全表删除的痛苦经历,恢复业务数据的过程真的太痛苦了,尤其与交易和钱相关的数据,必须恢复成和原来一模一样,那能不能在数据库层面架起最后一道安全堡垒,拒绝全表更新,全表删除的非法操作呢,答案是有的,在mysql中sql_safe_updates可以完美解决这个问题;

MySQL数据库是可以开启安全模式,不过默认情况下,安全模式不开启的,下面就来说说什么是mysql的安全模式,如下是sql_safe_updates参数的官方解释:

If this variable is enabled, UPDATE and DELETE statements that do not use a key in the WHERE clause or a LIMIT clause produce an error. This makes it possible to catch UPDATE and DELETE statements where keys are not used properly and that would probably change or delete a large number of rows. The default value is OFF.

sql_safe_updates默认是不开启的

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

    现在就开启这个参数,如果要永久生效,需要将参数添加到数据库配置文件(my.cnf)中

      mysql> set global sql_safe_updates=1;
      Query OK, 0 rows affected (0.00 sec)
      复制

      注意:需要重新连接一下数据库,才会生效;

      下面就来给大家演示一下实际效果,以及在MySQL 5.7以及8.0版本在开启该参数之后进行更新和删除操作的差异;

        mysql> CREATE TABLE `t_test1` (
        `id` int NOT NULL AUTO_INCREMENT,
        `name` varchar(20) DEFAULT NULL,
        `age` int DEFAULT NULL,
        PRIMARY KEY (`id`),
        KEY `idx_age` (`age`)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

        插入语句
        insert into t_test1 values(NULL,'test1',21);
        insert into t_test1 values(NULL,'test2',22);
        insert into t_test1 values(NULL,'test3',23);
        insert into t_test1 values(NULL,'test4',24);
        insert into t_test1 values(NULL,'test5',25);
        insert into t_test1 values(NULL,'test6',26);
        insert into t_test1 values(NULL,'test7',27);
        insert into t_test1 values(NULL,'test8',28);
        复制

        测试一下全表删除

          mysql> delete from t_test1;
          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> delete from t_test1 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.
          复制

          从上面的结果看,全部被数据库安全策略拦截了 再来测试一下更新

            mysql> update t_test1 set name='test';
            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> update t_test1 set name='test' 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.
            复制

            不带条件的更新也被拦截,那测试一下正常带条件的更新和删除看看效果

              mysql> update t_test1 set name='test' where name='test1';
              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> delete from t_test1 where name='test2';
              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数据库安全策略拦截了呢?

              下面针对update操作以及delete操作不同场景下的报错以及执行条件测试:

              1、update操作

              a、报错条件:不带where、带where无索引、where条件为常量

              (1)不带where:update t_test1 set name=’test’;

              (2)带where无索引:update t_test1 set name=’test’ where name=’test1′;

              (3)where条件为常量:update t_test1 set name=’test’ where 1=1;

              b、执行条件:带where带索引、不带where+带limit、带where无索引+limit、带where有索引+limit、where条件为常量+limit

              (1)带where带索引:update t_test1 set name=’test’ where age=21;

              (2)不带where+带limit:update t_test1 set name=’test’ limit 1;

              (3)带where无索引+limit:update t_test1 set name=’test’ where name=’test1′ limit 1;

              (4)带where有索引+limit:update t_test1 set name=’test’ where age=21 limit 1;

              (5)where条件为常量+limit:update t_test1 set name=’test’ where 1=1 limit 1;

              2、delete操作

              相对于update,delelte的限制会更为严格;并且在5.7和8.0版本存在一点差异。

              a、报错条件:不带where、带where无索引、不带where+带limit、where条件为常量、where条件为常量+limit

              不带where:delete from t_test1;

              带where无索引:delete from t_test1 where name=’test’;

              where条件为常量:delete from t_test1 where 1=1;

              b、执行条件:带where带索引、带where无索引+limit、带where有索引+limt

              带where带索引:delete from t_test1 where age=22;

              不带where+带limit:delete from t_test1 limit 1; (5.7版本执行会报错,8.0版本是可以执行成功的)

              带where无索引+limit:delete from t_test1 where name=’test’ limit 1;

              带where有索引+limit:delete from t_test1 where age=22 limit 1;

              where条件为常量+limit:delete from t_test1 where 1=1 limit 1;(5.7版本执行会报错,8.0版本是可以执行成功的)

              大家重点关注上面粉色的条件(delete from t_test1 limit 1)和(delete from t_test1 where 1=1 limit 1;)这两种在5.7和8.0的执行情况是不一样的,在5.7版本,满足报错条件,会执行失败;而在8.0版本,满足执行调整,可以执行成功;


              不知大家在使用MySQL Workbench操作数据库的时候是否注意到,Workbench默认是开启安全模式的,否则你的删除操作就会出现类型如下的报错信息:

              从具体的报错信息中,也是可以看到原因以及解决方案:Edit–>Perferences–>SQL Editor

              在实际通过workbench连接生产环境数据库的时候,是强烈不建议关闭的,避免人为导致全表删除或全表更新的操作;

                总结

              如果设置了sql_safe_updates=1,

              update语句必须满足如下条件之一才能执行成功

              1)使用where子句,并且where子句中列必须为索引列

              2)使用limit

              3)同时使用where子句和limit(此时where子句中列可以不是索引列)

              delete语句,5.7和8.0版本有些差异,必须满足如下条件之一才能执行成功

              5.7版本:

              1)使用where子句,并且where子句中列必须为索引列

              2)同时使用where子句和limit(此时where子句中列可以不是索引列)

              8.0版本:

              1)使用where子句,并且where子句中列必须为索引列

              2)使用limit

              3)同时使用where子句和limit(此时where子句中列可以不是索引列)

              在生产环境中,建议开启该参数设置,这样不仅可以避免全表更新或删除的操作,也可以引导开发人员为where条件过滤的字段添加索引等;

              最后修改时间:2021-07-09 16:02:40
              文章转载自DBA的辛酸事儿,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

              评论