MySQL运维之神奇的参数

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: MySQL运维之神奇的参数 sql_safe_updateshttp://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_sql_safe_updates 背景(why) 主要是针对大表的误操作。

MySQL运维之神奇的参数

sql_safe_updates
http://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_sql_safe_updates

背景(why)

主要是针对大表的误操作。

如果只是更改了几条记录,那么说不定业务方可以很容易的根据日志进行恢复。即便没有,也可以通过找binlog,进行逆向操作恢复。

如果被误操作的表非常小,其实问题也不大,全备+binlog恢复 or 闪回 都可以进行很好的恢复。

But,如果你要恢复的表非常大,比如:100G,100T,对于这类型的误操作,恐怕神仙都难救。

所以,我们这里通过这个神奇的参数,可以避免掉80%的误操作场景。 PS: 不能避免100% ,下面的实战会告诉大家如何破解。

生产环境的误操作案例分享


update xx set url_desc='防不胜防' WHERE 4918=4918 AND SLEEP(5)-- xYpp' where id=7046

这种表,线上有500G,一次误操作,要恢复500G的数据,会中断服务很长时间。

如果设置了sql_safe_updates,此类事故就可以很华丽的避免掉了。

原理和实战

  • 表结构
dba:lc> show create table tb;
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                                                           |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| tb    | CREATE TABLE `tb` (
  `id` int(11) NOT NULL,
  `id_2` int(11) DEFAULT NULL COMMENT 'lc22222233333',
  `id_3` text,
  PRIMARY KEY (`id`),
  KEY `idx_2` (`id_2`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
  • update 相关测试

UPDATE statements must have a WHERE clause that uses a key or a LIMIT clause, or both.

* 不带where 条件

dba:lc> update tb set id_2=2 ;
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

dba:lc> update tb set id_3 = 'bb' where id > 0;
^C^C -- query aborted
ERROR 1317 (70100): Query execution was interrupted

* where 条件无索引,也没有limit

dba:lc> update tb set id_3 = 'bb' where id_3 = '0';
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

dba:lc> update tb set id_3 = 'bb' where id > 0 limit 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0


* where 条件无索引,有limit

dba:lc> update tb set id_3 = 'bb' where id_3 > 0 limit 1;
Query OK, 0 rows affected (0.26 sec)
Rows matched: 0  Changed: 0  Warnings: 0


结论: 对于update,只有两种场景会被限制

  1. 无索引,无limit的情况
  2. 无where条件, 无limit的情况
  • delete相关测试

DELETE statements must have both

* 不带where 条件

dba:lc> delete from tb ;
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

dba:lc> delete from tb where id = 0 ;
Query OK, 0 rows affected (0.00 sec)

dba:lc> delete from tb where id > 0 ;
^C^C -- query aborted
ERROR 1317 (70100): Query execution was interrupted

dba:lc> explain select * from tb where id_2 > 0;
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | tb    | NULL       | ALL  | idx_2,idx_3   | NULL | NULL    | NULL | 245204 |    50.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

dba:lc> delete from tb where id_2 > 0 ;
^C^C -- query aborted
^C^C -- query aborted
ERROR 1317 (70100): Query execution was interrupted


* where 条件无索引,也没有limit

dba:lc> delete from tb where id_3 = 'a' ;
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

dba:lc> delete from tb where id = 205 limit 1 ;
Query OK, 1 row affected (0.00 sec)

* where 条件无索引,有limit

dba:lc> delete from tb where id_3 = 'aaaaa' limit 1 ;
Query OK, 1 row affected (0.00 sec)

测试结果证明: 关于delete相关,官方文档描述有误。

结论: 对于delete,只有两种场景会被限制

  1. 无索引,无limit的情况
  2. 无where条件, 无limit的情况

综上所述:不管是update,还是delete ,被限制的前提只有两个

1. 无索引,无limit的情况
2. 无where条件, 无limit的情况

好了,通过以上的知识,大家都应该很了解,接下来就是实施的问题了。

对于新业务,新DB,直接设置这样的参数就好了,再测试环境也设置,这样开发在测试环境就能发现问题,不会在新业务上产生这样危险的语句。

对于老业务,怎么办呢?

我们的做法:因为我们的MySQL是5.6,所以另外一个神奇的功能就是P_S(performance schema), 通过P_S,我们可以获取哪些query语句是没有使用索引的。

这里又会引发另外一个问题,可能是Performance schema的bug,它竟然无法统计dml 是否使用索引

经过我们大量的测试后证明:events_statements_summary_by_digest 表里面的SUM_NO_INDEX_USED,SUM_NO_GOOD_INDEX_USED ,对dml无效。

既然如此,我们所幸对dml语句自己进行分析,将dml转换成对应的select语句。
比如: update tb set id = S where id = S; 转换成 select * from tb where id = '1' 。。。。

然后根据select语句,进行explain分析,如果type=ALL表示没有使用索引,这样的语句就是我们认为的全表dml语句了。

然而,理想很丰满,现实很骨感。这样的做法很快就出现了问题, 因为这里需要自己构造真实的SQL,由于数据分布以及构造的语句不可能真实,所以得到的执行计划谬之千里,type=None。

所以,以上方法很可能导致全表的dml没有被抓取出来,so 我们开始想其他办法。

说来也简单,sql_safe_udpates 只针对两种场景是不允许的,那就是:

1. where条件后面 无索引,无limit的情况
2. 无where条件的情况 , 无limit的情况

那么我们就获取dml语句后面的字段和关键字,用来构造我们的全表dml

1. 检查dml 是否是带有limit的语句
    如果有,允许通过 -- ( 有limit , 肯定可以执行  )
    如果没有,则往下继续判断

2. 判断dml SQL有无where条件
    如果没有, 则直接拒绝  -- (没有where,没有limit,肯定是全表扫描的更新,直接拒绝 )
    如果有,则继续往下判断

3. 判断where后面的字段是否符合索引前缀原理
    如果符合,则允许执行  -- ( where条件后面字段有索引,无limit, 允许通过 )
    如果不符合,则拒绝   -- ( where条件后面字段无索引,无limit,直接拒绝  )

恩,这样分析下来,是不是感觉很完美了? 还是那句话,理想和现实总有差距,那么来几条牛逼的漏网之鱼看看呗

1. 类型转换导致的问题

update tb set id=2 where id_change = 1;  -- 注意:字段id_change是varchar类型。

2. 函数

UPDATE pay_log_id SET id=LAST_INSERT_ID(id + 1)

至少以上两种类型是抓不到的,所以,还是有问题,那么继续找方法。

重新分析下我们的初心,我们的目的是啥?没错,我们的目的就是要先找到没有使用索引的dml,突然脑海中飘来一句话,MySQL自身是否可以打印出没有使用索引的语句呢?

果然,去官方文档上一搜index关键字,结果log_queries_not_using_indexes就是我们迫切需要的,但是它会将select也打印出来,不过没关系,我们将select过滤掉即可。

so,最后的终极解决方案就是:在测试环境加上log_queries_not_using_indexes=1(long_query_time=1000,这样可以不用混淆),然后测试环境跑一个月,将没有使用索引的dml语句统统抓住来解决掉,这样就可以安心的上线sql_safe_updates=1 了。

注意:

当log_queries_not_using_indexes=1 和 sql_safe_updates=1 同时设置的时候:

1) delete from tb_1 ;  --会被sql_safe_updates拒绝,不会记录到slow log中

2) update tb_1 set id = 1; --会被sql_safe_updates拒绝,同时也会被记录到slow log中

以上就是两者的区别,善用

总结

如果线上设置sql_safe_updates = 1 后,业务还有零星的dml被拒绝,业务方可以考虑如下解决方案:

1)如果你确保你的SQL语句没有任何问题,可以这样: set sql_safe_updates=0; 但是开发必须考虑到这样做的后果。

2) 可以改写SQL语句,让其使用上索引字段。

3)为什么这边没有让大家使用limit呢?因为在大多数场景下,dml + limit = 不确定的SQL 。 很可能导致主从不一致。 ( dml + limit 的方式,是线上禁止的)

各位看官,以上神器请大家慢慢享用。 关于PS和sys,如果大家有更加新奇的想法,可以一起讨论研究。

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
4天前
|
存储 缓存 关系型数据库
MySQL底层概述—5.InnoDB参数优化
本文介绍了MySQL数据库中与内存、日志和IO线程相关的参数优化,旨在提升数据库性能。主要内容包括: 1. 内存相关参数优化:缓冲池内存大小配置、配置多个Buffer Pool实例、Chunk大小配置、InnoDB缓存性能评估、Page管理相关参数、Change Buffer相关参数优化。 2. 日志相关参数优化:日志缓冲区配置、日志文件参数优化。 3. IO线程相关参数优化: 查询缓存参数、脏页刷盘参数、LRU链表参数、脏页刷盘相关参数。
MySQL底层概述—5.InnoDB参数优化
|
2月前
|
SQL 存储 关系型数据库
MySQL进阶突击系列(01)一条简单SQL搞懂MySQL架构原理 | 含实用命令参数集
本文从MySQL的架构原理出发,详细介绍其SQL查询的全过程,涵盖客户端发起SQL查询、服务端SQL接口、解析器、优化器、存储引擎及日志数据等内容。同时提供了MySQL常用的管理命令参数集,帮助读者深入了解MySQL的技术细节和优化方法。
|
3月前
|
关系型数据库 MySQL 数据库
【赵渝强老师】MySQL的参数文件
MySQL启动时会读取配置文件my.cnf来确定数据库文件位置及初始化参数。该文件分为Server和Client两部分,包含动态与静态参数。动态参数可在运行中通过命令修改,而静态参数需修改my.cnf并重启服务生效。文中还提供了相关代码示例和视频教程。
114 6
|
3月前
|
缓存 监控 关系型数据库
如何根据监控结果调整 MySQL 数据库的参数以提高性能?
【10月更文挑战第28天】根据MySQL数据库的监控结果来调整参数以提高性能,需要综合考虑多个方面的因素
130 1
|
4月前
|
运维 关系型数据库 MySQL
运维|MySQL 数据库被黑,心力交瘁
前一阵有一个测试用的 MySQL 数据库被黑了,删库勒索的那种,这里记录一下事情经过,给自己也敲个警钟。
67 2
|
5月前
|
存储 SQL 关系型数据库
【MySQL调优】如何进行MySQL调优?从参数、数据建模、索引、SQL语句等方向,三万字详细解读MySQL的性能优化方案(2024版)
MySQL调优主要分为三个步骤:监控报警、排查慢SQL、MySQL调优。 排查慢SQL:开启慢查询日志 、找出最慢的几条SQL、分析查询计划 。 MySQL调优: 基础优化:缓存优化、硬件优化、参数优化、定期清理垃圾、使用合适的存储引擎、读写分离、分库分表; 表设计优化:数据类型优化、冷热数据分表等。 索引优化:考虑索引失效的11个场景、遵循索引设计原则、连接查询优化、排序优化、深分页查询优化、覆盖索引、索引下推、用普通索引等。 SQL优化。
843 15
【MySQL调优】如何进行MySQL调优?从参数、数据建模、索引、SQL语句等方向,三万字详细解读MySQL的性能优化方案(2024版)
|
4月前
|
SQL 运维 关系型数据库
MySQL 运维 SQL 备忘
MySQL 运维 SQL 备忘录
65 1
|
4月前
|
SQL 关系型数据库 MySQL
数据库:MYSQL参数max_allowed_packet 介绍
数据库:MYSQL参数max_allowed_packet 介绍
518 2
|
6月前
|
运维 关系型数据库 MySQL
在Linux中,MySQL数据库日常运维中涉及哪些关键任务?
在Linux中,MySQL数据库日常运维中涉及哪些关键任务?
|
23天前
|
关系型数据库 MySQL 数据库连接
数据库连接工具连接mysql提示:“Host ‘172.23.0.1‘ is not allowed to connect to this MySQL server“
docker-compose部署mysql8服务后,连接时提示不允许连接问题解决

推荐镜像

更多