关于mysql:卧槽线上数据删错了差点被老板开除

41次阅读

共计 5209 个字符,预计需要花费 14 分钟才能阅读完成。

苏三说技术

「苏三说技术」维护者目前就任于某出名互联网公司,从事开发、架构和局部管理工作。实战经验丰盛,对 jdk、spring、springboot、springcloud、mybatis 等开源框架源码有肯定钻研,欢送关注,和我一起交换。

29 篇原创内容

公众号

前言

=====

无论是开发、测试,还是 DBA,都难免会波及到数据库的操作,比方:创立某张表,增加某个字段、增加数据、更新数据、删除数据、查问数据等等。

失常状况下还好,但如果操作数据库时呈现失误,比方:

  1. 删除订单数据时 where 条件写错了,导致多删了很多用户订单。
  2. 更新会员无效工夫时,一次性把所有会员的无效工夫都更新了。
  3. 修复线上数据时,改错了,想还原。

还有很多很多场景,我就不一一列举了。

如果呈现线上环境数据库误操作怎么办?有没有后悔药?

答案是有的,请各位看官认真往下看。

1. 不要用聊天工具发 sql 语句

通常开发人员写好 sql 语句之后,习惯通过聊天工具,比方:qq、钉钉、或者腾讯通等,发给 团队老大 或者 DBA 在线上环境执行。但因为有些聊天工具,对局部 特殊字符 会主动本义,而且有些音讯因为 内容太长,会被主动分成多条音讯。

这样会导致团队老大或者 DBA 复制进去的 sql 不肯定是正确的。

他们须要手动拼接成一条 残缺的 sql,有时甚至须要把本义后的字符替换回以前的特殊字符,无形之中会节约很多额定的工夫。即便最终 sql 拼接好了,真正执行 sql 的人,心里肯定很虚。

所以,强烈建议你把要在线上执行的 sql 语句用 邮件 发过来,能够防止应用聊天工具的一些弊病,缩小一些误操作的机会。而且有个存档,不便今后有问题的时候回溯起因。很多聊天工具只保留最近 7 天 的历史记录,邮件会保留更久一些。

别用聊天工具发 sql 语句!

别用聊天工具发 sql 语句!

别用聊天工具发 sql 语句!

重要的事件说三遍,它真的能缩小一些误操作。

2. 把 sql 语句压缩成一行

有些时候,开发人员写的 sql 语句很长,应用了各种 joinunion,而且应用丑化工具,将一条 sql 变成了多行。在复制 sql 的时候,本人都无奈确定 sql 是否残缺。(为了装逼,把本人也坑了,哈哈哈)

线上环境有时候须要通过命令行连贯数据库,比方:mysql,你把 sql 语句复制过去后,在命令行界面执行,因为屏幕滚动太快,这时根本无法确定 sql 是否都执行胜利。

针对这类问题,强烈建议把 sql 语句 压缩成一行 ,去掉多余的 换行符 空格,能够无效的缩小一些误操作。

sql 压缩工具 举荐应用:https://tool.lu/sql/

3. 操作数据之前先 select 一下

须要特地阐明的是:本文的操作数据次要指 批改 删除 数据。

很多时候,因为咱们人为失误,把 where 条件写错了。但没有怎么仔细检查,就把 sql 语句间接执行了。影响范畴小还好,如果影响几万、几十万,甚至几百万行数据,咱们可能要哭了。

针对这种状况,在操作数据之前,把 sql 先改成 select count(*) 语句,比方:

update order set status=1 where status=0;

改成:

select count(*) from order where status=0;

查一下该 sql 执行后影响的记录行数,做到本人成竹在胸。也给本人一次测试 sql 是否正确,确认是否执行的机会。

4. 操作数据 sql 加 limit

即便通过下面的 select 语句确认了 sql 语句没有问题,执行后影响的记录行数是对的。

也倡议你不要立即执行,倡议在正在执行的时候,加上limit + select 出的记录行数。例如:

update order set status=1 where status=0 limit 1000;

假如有一次性更新的数据太多,所有相干记录行都会被锁住,造成长时间的锁期待,而造成用户申请超时。

此外,加 limit 能够防止一次性操作太多数据,对服务器的 cpu 造成影响。

还有一个最重要的起因:加 limit 后,操作数据的影响范畴是齐全可控的。

5.update 时更新批改人和批改工夫

很多人写 update 语句时,如果要批改状态,就只更新状态,不论其余的字段。比方:

update order set status=1 where status=0;

这条 sql 会把 status 等于 0 的数据,全副更新成 1。

起初发现业务逻辑有问题,不应该这么更新,须要把 status 状态回滚。

这时你可能会很天然想到这条 sql:

update order set status=0 where status=1;

但认真想想又有些不对。

这样不是会把有局部以前 status 就是 1 的数据更新成 0?

这回真的要哭了,呜呜呜。

这时,送你一个好习惯:在更新数据的时候,同时更新 批改人 批改工夫 字段。

update order set status=1,edit_date=now(),edit_user='admin' where status=0;
这样在复原数据时就能通过
批改人
和
批改工夫
字段过滤数据了。

前面须要用到的 批改工夫 通过这条 sql 语句能够轻松找到:

select edit_user ,edit_date from `order` order by edit_date desc limit 50;

当然,如果是高并发零碎不倡议这种批量更新形式,可能会锁表肯定工夫,造成申请超时。

有些同学可能会问:为什么要同时更新批改人,只更新批改工夫不行吗?

次要有如下的起因:

  1. 为了标识非正常用户操作,不便前面统计和定位问题。
  2. 有些状况下,在执行 sql 语句的过程中,失常用户产生数据的批改工夫跟你的可能截然不同,导致回滚时数据查多了。

6. 多用逻辑删除,少用物理删除

在业务开发中,删除数据是必不可少的一种业务场景。

有些人开发人员习惯将表设计成 物理删除 ,依据主键只用一条delete 语句就能轻松搞定。

他们给出的理由是:节俭数据库的存储空间

想法是好的,然而事实很残暴。

如果有条极重要的数据删错了,想复原怎么办?

此时只剩八个字:没有数据,复原不了。(PS:或者通过 binlog 二进制文件能够复原)

如果之前设计表的时候用的 逻辑删除 ,下面的问题就变得好办了。删除数据时,只需update 删除状态即可,例如:

update order set del_status=1,edit_date=now(),edit_user='admin' where id=123;

如果出现异常,要复原数据,把该 id 的删除状态还原即可,例如:

update order set del_status=0,edit_date=now(),edit_user='admin' where id=123;

7. 操作数据之前先做备份

如果只是批改了大量的数据,或者只执行了一两条 sql 语句,通过下面的 批改人 批改工夫 字段,在须要回滚时,能疾速的定位到正确的数据。

然而如果批改的记录行数很多,并且执行了多条 sql,产生了很多 批改工夫。这时,你可能就要犯难了,没法一次性找出哪些数据须要回滚。

为了解决这类问题,能够将表做备份。

能够应用如下 sql 备份:

create table order\_bak\_2021031721 like\`order\`;
insert into order\_bak\_2021031721 select \* from\`order\`;

先创立一张截然不同的表,而后把数据复制到新表中。

也能够简化成一条 sql:

create table order_bak_2021031722 select * from`order`;
` 创立表的同时复制数据到新表中。`

此外,倡议在表名中加上 bak工夫,一方面是为了通过表名疾速辨认出哪些表是备份表,另一方面是为了备份屡次时好做辨别。因为有时须要执行屡次 sql 能力把数据修复好,这种状况倡议把表备份屡次,如果出现异常,把数据回滚到最近的一次备份,能够节俭很多反复操作的工夫。

复原数据时,把 sql 语句改成 select 语句,先在备份库找出相干数据,每条数据对应一条 update 语句,还原到老表中。

8. 两头后果写入长期表

有时候,咱们要先用一条 sql 查问出要更新的记录的 id,而后通过这些 id 更新数据。

批量更新之后,发现不对,要回滚数据。但因为有些数据已更新,此时应用雷同的 sql 雷同的条件,却查不出上次雷同的 id 了。

这时,咱们开始慌了。

针对这种状况,咱们能够先将第一次查问的 id 存入一张 长期表 ,而后通过 长期表 中的 id 作为查问条件更新数据。

如果要复原数据,只用通过 长期 表中的 id 作为查问条件更新数据即可。

批改完,3 天之后,如果没有呈现问题,就能够把 长期表 删掉了。

9. 表名后面肯定要带库名

咱们在写 sql 时为了不便,习惯性不带数据库名称。比方:

update order set status=1,edit_date=now(),edit_user='admin' where status=0;
如果有多个数据库中有雷同的表
order,表构造截然不同,只是数据不一样。

因为执行 sql 语句的人一个小失误,进错数据库了。

use trade1;

而后执行了这条 sql 语句,后果喜剧了。

有个十分无效的预防这类问题的办法是加 数据库名

update `trade2`.`order` set status=1,edit_date=now(),edit_user='admin' where status=0;
` 这样即便执行 sql 语句后退错数据库了,也没什么影响。`

10. 字段增删改的限度

很多时候,咱们少不了对表字段的操作,比方:新加、批改、删除字段,但每种状况都不一样。

新加的字段肯定要容许为空

新加的字段肯定要容许为空。为什么要这样设计呢?

失常状况下,如果程序新加了字段,个别是先在数据库中加字段,而后再发程序的最新代码。

为什么是这种程序?

因为如果先发程序,而后在数据库中加字段。在该程序刚部署胜利,但数据库新字段还没来得及加的这段时间内,最新程序中,所有应用了新加字段的增删改查 sql 都会报字段不存在的异样。

好了,就按先在数据库中加字段,再发程序的程序。

如果数据库中新加的字段非空,最新的程序还没发,线上跑的还是老代码,这时如果有 insert 操作,就会报字段不能为空的异样。因为新加的非空字段,老代码是没法赋值的。

所以说新加的字段肯定要容许为空。

除此之外,这种设计更多的思考是为了程序公布失败时的回滚操作。如果新加的字段容许为空,则能够不必回滚数据库,只需回滚代码即可,是不是很不便?

不容许删除字段

删除字段是不容许的,特地是必填字段肯定不能删除。

为什么这么说?

假如开发人员曾经把程序改成不应用删除字段了,接下来如何部署呢?

  1. 如果先把程序部署好了,还没来得及删除数据库相干表字段。当有 insert 申请时,因为数据库中该字段是必填的,会报必填字段不能为空的异样。
  2. 如果先把数据库中相干表字段删了,程序还没来得及发。这时所有波及该删除字段的 增删改查,都会报字段不存在的异样。

所以,线上环境必填字段肯定不能删除的。

依据理论状况批改字段

批改字段要分为这三种状况:

1. 批改字段名称

批改字段名称也不容许,跟删除必填字段的问题差不多。

  1. 如果把程序部署好了,还没来得及批改数据库中表字段名称。这时所有波及该字段的 增删改查,都会报字段不存在的异样。
  2. 如果先把数据库中字段名称改了,程序还没来得及发。这时所有波及该字段的 增删改查,同样也会报字段不存在的异样。

所以,线上环境字段名称肯定不要批改。

2. 批改字段类型

批改字段类型时肯定要兼容之前的数据。例如:

  1. tinyint 改成 int 能够,但 int 改成 tinyint 要认真掂量一下。
  2. varchar 改成 text 能够,但 text 改成 varchar 要认真掂量一下。

3. 批改字段长度

字段长度倡议改大,通常状况下,不倡议改小。如果肯定要改小,要先确认该字段可能会呈现的最大长度,防止 insert 操作时呈现字段太长的异样。

此外,倡议改大也须要设置一个正当的长度,防止数据库资源节约。

总结

本文分享了 10 种缩小数据库误操作的办法,并非所有场景都适宜你。特地是在一些高并发,或者单表数据量十分大的场景,你须要依据理论状况酌情抉择。但我敢肯定的是读完这篇文章,你肯定会有一些播种,因为大部分办法对你来说是实用的,可能会让你少走很多弯路,强烈建议珍藏。

最初说一句(求关注,别白嫖我)

如果这篇文章对您有所帮忙,或者有所启发的话,帮忙关注一下,您的反对是我保持写作最大的能源。

求一键三连:点赞、转发、在看。

关注公众号:【苏三说技术】,在公众号中回复:面试、代码神器、开发手册、工夫治理有超赞的粉丝福利,另外回复:加群,能够跟很多 BAT 大厂的前辈交换和学习。

苏三说技术

「苏三说技术」维护者目前就任于某出名互联网公司,从事开发、架构和局部管理工作。实战经验丰盛,对 jdk、spring、springboot、springcloud、mybatis 等开源框架源码有肯定钻研,欢送关注,和我一起交换。

29 篇原创内容

公众号

正文完
 0