iis服务器助手广告广告
返回顶部
首页 > 资讯 > 数据库 >MySQL问题两则
  • 736
分享到

MySQL问题两则

2024-04-02 19:04:59 736人浏览 薄情痞子
摘要

1 | 前言 近期在给开发小伙伴解决问题的时候,收集了两个个人觉得比较有意思的问题给大家分享一下。 一个是在执行ALTER TABLE ADD COLUMN语句时,报了Duplicate entry

1 | 前言

近期在给开发小伙伴解决问题的时候,收集了两个个人觉得比较有意思的问题给大家分享一下。 一个是在执行ALTER TABLE ADD COLUMN语句时,报了Duplicate entry的错误; 另一个是关于在Mysql中正确存取emoji表情的问题。

2 | ALTER TABLE ADD COLUMN报Duplicate entry错误

2.1 问题描述

某日系统上线,接到开发小伙伴电话说在上线时,执行一个增加字段的DDL语句脚本时,报错了,错误如下:
ERROR 1062 (23000) at line 1: Duplicate entry 'UR000021426347' for key 'T_CAP_CUST_MIDDLE_INFO_UNIQ_INDEX' 
根据错误提示的条件去数据库中查询却只能查到一条记录,并没有重复记录。 DDL脚本无法执行,影响后续上线步骤了。 当时由于不在现场,了解到的信息只有:

  1. DDL语句脚本中只有两条DDL语句,且都是添加字段的语句。

  2. 脚本正常运行只需要40-70秒。

  3. 当时并不是停业窗口。

考虑到数据库版本为5.6.34,添加字段并不会阻塞DML操作,让开发小伙伴再运行一次试试,结果这次执行成功了,并没有报冲突的错误。 线上问题顺利解决,具体原因得线下分析了。 虽说解决问题是主要矛盾,但是搞清楚问题原因有着更深层次的意义。

2.2 原因定位

下面就是到了寻找问题原因的时候了,为什么同样的DDL语句脚本第一次执行的时候报了Duplicate entry错误,第二次却顺利运行了。 其实问题原因很好找,打开Google,输入关键字 mysql alter table add column duplicate entry ,搜索结果中很多关键字完全匹配的链接,说明很多人遇到过相同问题。 搜索结果中一眼就看到一个链接 Mysql Bugs:#76895:Adding new column OR Drop column causes duplicate PK error ,看到MySQL Bug就莫名兴奋。 通过该Bug链接了解到该问题是Online DDL的一个限制问题,官方认为该问题是一种限制,并不是Bug,所以目前为止还没有得到解决。

When running an online DDL operation, the thread that runs the ALTER TABLE statement applies an online log of DML operations that were run concurrently on the same table from other connection threads. When the DML operations are applied, it is possible to encounter a duplicate key entry error (ERROR 1062 (23000): Duplicate entry), even if the duplicate entry is only temporary and would be reverted by a later entry in the online log. This is similar to the idea of a foreign key constraint check in InnoDB in which constraints must hold during a transaction.
 解释一下就是当执行Oline DDL操作时,MySQL实际上是将DML缓存(该缓存大小由变量 innodb_online_alter_log_max_size控制,默认128M)起来,等DDL执行完成后再将缓存中的DML重新应用到表上。 如果有别的线程执行了DML操作,在DDL完成后,应用DML时,可能会出现duplicate entry错误。

2.3 实验验证

上面通过Google找到了理论上可能能解释问题的原因描述,但是还没有实际验证,所以接下来就是线下复现环节。先去找开发同事问了下线上报错的表只有一种操作 insert into ... on duplicate key... ,且报Duplicate entry的字段上有唯一索引。如果没有冲突的记录则插入,否则就更新。那么验证测试步骤也比较简单了,找一张测试表,执行ALTER TABLE ADD COLUMN操作,并同时执行insert into...on duplicate key...操作,观察DDL语句是否会有报错。

2.3.1 实验环境
  • redhat-6.7/redhat-7.4

  • MySQL-5.6.34/MySQL-5.7.22

2.3.2 操作步骤
  • 准备测试环境。

MySQL问题两则 MySQL问题两则

col1字段的值与id字段的值是一致的,test表共有1600W+条记录。

  • 执行DDL同时执行insert into ... on duplicate key...操作。
MySQL问题两则

顺利的复现了线上的问题现象,那说明当时线上就是因为DML更新了相同的唯一属性字段键值导致DDL执行失败,报错。 测试过程中想到insert into... on duplicate key...不行,那么replace into 会不会也一样导致问题呢,于是就同样对replace into语句进行了测试。

  • 执行DDL同时执行replace into操作。

MySQL问题两则

  • 后续以同样方式测试了UPDATE、INSERT操作,实验证明都会影响DDL操作的正确执行,在5.7.22版本上也是一样的现象。 这边出于篇幅考虑就不将测试过程给出了,感兴趣的读者可以自己实验一把,并且可以验证下在8.0版本中是否仍然存在该问题。

2.4 小结

在这对这个问题做几点总结

  • 问题原因: Oline DDL的原理简单一点理解就是将DML操作缓存起来,等到DDL执行完成后重新应用缓存中的DML语句,如果在Oline DDL执行过程中,DML操作产生了Duplicate entry错误,并不会直接影响DDL操作,而是在DDL执行完成最终应用DML时报错,导致DDL执行失败。 关于Oline DDL执行步骤可以参考: https://yq.aliyun.com/articles/282290 。

  • 在MySQL Bug网站上,官方人员回复该现象并不是Bug,而是一种限制。 但是个人认为是可以做一些改善的,因为在测试insert into ... on duplicate key...以及update和insert语句时,对于执行DML操作的客户端已经直接返回报错了,但是从现象上看MySQL仍然将报错的DML语句放到了Oline DDL的缓存中,如果直接将报错语句从缓存中去除则不会影响DDL的正常执行。 这只是个人简单认为,深入的话需要可以通过代码去确认。

  • Google是位好老师。

3 | MySQL存取emoji的正确姿势

3.1 问题描述

开发小伙伴在测试环境测试过程中,需要往MySQL数据库中插入emoji表情,但是发现一套测试环境可以插入成功,另一套测试环境不行,且插入成功的那套环境数据库将数据查询出来是 ???? 乱码。经过与开发确认后了解到以下几点信息:

  • 表中只有一个字段需要存放emoji表情,开发对该字段单独设置了utf8mb4字符集。

  • 可以成功插入成功的测试环境数据库版本为5.6.34,无法插入的测试环境数据库版本为5.7.22。

  • 应用连接代码中使用的是utf8字符集。

那其实需要解决的问题其实是两个问题: 1、5.6版本下emoji存取乱码问题 2、5.7版本emoji无法插入的问题。

3.2 实验验证

3.2.1 MySQL- 5.6 emoji存取乱码问题。

我们先来看5.6版本下emoji存储乱码的问题,理论上从数据库角度考虑,字段字符集已经设置为utf8mb4,应用使用的是utf8字符集连接,插入emoji需要utf8mb4字符集,多半是连接字符集设置的问题。 大胆假设已经完成,接下来就是小心求证的过程。

实验环境

  • 数据库版本: 5.6.44

  • sql_mode='NO_ENGINE_SUBSTITUTION'

  • default-character-set=utf8

  • 使用utf8字符集连接MySQL,并检查参数设置。

MySQL问题两则

  • 创建测试。

MySQL问题两则

  • 插入emoji表情。

MySQL问题两则 MySQL问题两则 表情的确能插入数据库,但是insert时有两条warning记录提示是invalid的字符串,并且select查询出来的数据也是一样 ???? 乱码。

  • 使用utf8mb4字符集插入emoji表情并查询。

MySQL问题两则

可以看到连接使用utf8mb4字符集插入、查询emoji表情都是正常的,插入时也没有warning提示,那说明emoji乱码的问题就是因为连接字符集设置不合理导致的。
3.2.2 MySQL-5.7 emoji无法插入的问题

定位了乱码的问题,再来看看emoji无法插入的问题。

实验环境

  • 数据库版本: 5.7.22

  • sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'

  • default-character-set=utf8

  • 使用utf8字符集连接MySQL,并检查参数设置。

MySQL问题两则

  • 创建测试。

MySQL问题两则

  • 插入emoji表情。

MySQL问题两则

插入的时候直接报错了,同样的字符集,同样的语句,5.6版本下emoji就能插入,5.7就直接报错了。 5.6->5.7数据库版本问题,如果踩坑比较多的话,还是比较容易联想到是不是sql_mode参数的问题,因为5.7中sql_mode参数默认值多了很多项,对语句的限制加强了很多。
  • 查看sql_mode参数默认值。
mysql> show variables like '%sql_mode%'\G
*************************** 1. row ***************************
Variable_name: sql_mode
    Value: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
1 row in set (0.00 sec)

查看官方文档关于各个sql_mode选项的值,找到了怀疑对象STRICT_TRANS_TABLES,表示开启严格模式,严格模式下如果插入的数据不在范围之类会报错中断语句。

  • 修改sql_mode参数值,再次插入emoji表情测试。
mysql> set session sql_mode='ONLY_FULL_GROUP_BY,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
Query OK, 0 rows affected, 1 warning (0.00 sec

MySQL问题两则

可以看到修改sql_mode值后,现象与5.6版本下一致。

使用utf8mb4字 符集插入emoji表情并查询。

MySQL问题两则

3.3 小结

在此对emoji问题做一下小结:

  • MySQL中存储emoji不仅需要将表结构中字段字符集设置为utf8mb4,还需要考虑连接字符集的问题。

  • 建议数据库使用5.7.22及以上版本,并且sql_mode开启严格模式,这样当数据出现异常可以及时发现。

  • 在sql_mode开启严格模式的情况下,应用端连接数据库也需要调整为utf8mb4字符集才可以正常插入emoji表情。

| 作者简介

沈 刚·沃趣科技数据库技术专家

熟悉MySQL数据库运行机制,丰富的数据库及复制架构故障诊断、性能调优、数据库备份恢复及迁移经验。

您可能感兴趣的文档:

--结束END--

本文标题: MySQL问题两则

本文链接: https://www.lsjlt.com/news/46145.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • oracle怎么查询当前用户所有的表
    要查询当前用户拥有的所有表,可以使用以下 sql 命令:select * from user_tables; 如何查询当前用户拥有的所有表 要查询当前用户拥有的所有表,可以使...
    99+
    2024-05-14
    oracle
  • oracle怎么备份表中数据
    oracle 表数据备份的方法包括:导出数据 (exp):将表数据导出到外部文件。导入数据 (imp):将导出文件中的数据导入表中。用户管理的备份 (umr):允许用户控制备份和恢复过程...
    99+
    2024-05-14
    oracle
  • oracle怎么做到数据实时备份
    oracle 实时备份通过持续保持数据库和事务日志的副本来实现数据保护,提供快速恢复。实现机制主要包括归档重做日志和 asm 卷管理系统。它最小化数据丢失、加快恢复时间、消除手动备份任务...
    99+
    2024-05-14
    oracle 数据丢失
  • oracle怎么查询所有的表空间
    要查询 oracle 中的所有表空间,可以使用 sql 语句 "select tablespace_name from dba_tablespaces",其中 dba_tabl...
    99+
    2024-05-14
    oracle
  • oracle怎么创建新用户并赋予权限设置
    答案:要创建 oracle 新用户,请执行以下步骤:以具有 create user 权限的用户身份登录;在 sql*plus 窗口中输入 create user identified ...
    99+
    2024-05-14
    oracle
  • oracle怎么建立新用户
    在 oracle 数据库中创建用户的方法:使用 sql*plus 连接数据库;使用 create user 语法创建新用户;根据用户需要授予权限;注销并重新登录以使更改生效。 如何在 ...
    99+
    2024-05-14
    oracle
  • oracle怎么创建新用户并赋予权限密码
    本教程详细介绍了如何使用 oracle 创建一个新用户并授予其权限:创建新用户并设置密码。授予对特定表的读写权限。授予创建序列的权限。根据需要授予其他权限。 如何使用 Oracle 创...
    99+
    2024-05-14
    oracle
  • oracle怎么查询时间段内的数据记录表
    在 oracle 数据库中查询指定时间段内的数据记录表,可以使用 between 操作符,用于比较日期或时间的范围。语法:select * from table_name wh...
    99+
    2024-05-14
    oracle
  • oracle怎么查看表的分区
    问题:如何查看 oracle 表的分区?步骤:查询数据字典视图 all_tab_partitions,指定表名。结果显示分区名称、上边界值和下边界值。 如何查看 Oracle 表的分区...
    99+
    2024-05-14
    oracle
  • oracle怎么导入dump文件
    要导入 dump 文件,请先停止 oracle 服务,然后使用 impdp 命令。步骤包括:停止 oracle 数据库服务。导航到 oracle 数据泵工具目录。使用 impdp 命令导...
    99+
    2024-05-14
    oracle
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作