Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >MySQL 线上2个小案例

MySQL 线上2个小案例

作者头像
AsiaYe
发布于 2022-12-07 01:03:36
发布于 2022-12-07 01:03:36
45100
代码可运行
举报
文章被收录于专栏:DBA随笔DBA随笔
运行总次数:0
代码可运行

MySQL 线上2个小案例

今天在线上遇到2个很有意思的MySQL案例,都是比较经典的问题,拿出来跟大家分享一下。为了对库表名称进行脱敏,我把问题抽象出来两个小的例子,且看分享。

01

索引长度导致的更新报错

来看这个案例:

首先我们有一张表:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 CREATE TABLE `t` (
  `id` int NOT NULL AUTO_INCREMENT,
  `col1` varchar() DEFAULT NULL,
  `col2` varchar() DEFAULT NULL,
  `col3` varchar() DEFAULT NULL,
  `col4` varchar() DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_c1_c2_c3_c4` (`col1`,`col2`,`col3`,`col4`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

表t有4个字段,分别是col1~col4;

唯一索引是由这4个字段组成的联合索引。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mysql> select * from t;
+----+------+------+------+------+
| id | col1 | col2 | col3 | col4 |
+----+------+------+------+------+
|  1 | a    | b    | c    | d    |
|  2 | aa   | bb   | cc   | dd   |
|  3 | aaa  | bbb  | ccc  | ddd  |
|  4 | aaaa | bbbb | cccc | dddd |
+----+------+------+------+------+
 rows in set (. sec)

表t中有4条记录,如上。

现在的问题是,col4需要更新一个数据,但是在更新数据的时候,发现长度太短了。报错如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mysql> update t set col4='ddddd' where id=;
ERROR  (): Data too long for column 'col4' at row 

这个其实不难理解,就是表t的col4这个字段设置的是varchar(4), 但是插入的数据是'ddddd'包含5个字符,当然会报一个Data too long的错误了。

业务同学看到这个报错之后,想着将这个表的col4字段改成varchar(5), 这样不就能进行更新了么?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mysql> alter table t modify col4 varchar();
ERROR  (): Specified key was too long; max key length is  bytes

可以看到,当我们尝试修改表t的col4字段的时候,发生了第二个报错,说定义的key太长了,最大的key长度是3072 bytes。

看起来我们也无法修改col4的定义了。我们看看是哪里出的问题,翻阅MySQL的官方文档,上面对这个现象有介绍:

For string columns, indexes can be created that use only the leading part of column values, using col_name(length) syntax to specify an index prefix length:

Prefix support and lengths of prefixes (where supported) are storage engine dependent. For example, a prefix can be up to 767 bytes long for InnoDB tables that use the REDUNDANT or COMPACT row format. The prefix length limit is 3072 bytes for InnoDB tables that use the DYNAMIC or COMPRESSED row format.

写的也很明白:

对于string类型的列,可以使用前缀字符来进行索引,

对于redundant或者compact行格式的记录,最大的索引前缀是767个字节;

对于dynamic和compressed的行格式的记录,最大的索引前缀长度是3072个字节

那我们看看当前表的行格式吧:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
select * from information_schema.tables where table_schema='test' and table_name='t'\G
*************************** 1. row ***************************
  TABLE_CATALOG: def
   TABLE_SCHEMA: test
     TABLE_NAME: t
     TABLE_TYPE: BASE TABLE
         ENGINE: InnoDB
        VERSION: 
     ROW_FORMAT: Dynamic
     TABLE_ROWS: 
 AVG_ROW_LENGTH: 
    DATA_LENGTH: 
MAX_DATA_LENGTH: 
   INDEX_LENGTH: 
      DATA_FREE: 
 AUTO_INCREMENT: 
    CREATE_TIME: 2022-07-07 ::
    UPDATE_TIME: 2022-07-07 ::
     CHECK_TIME: NULL
TABLE_COLLATION: utf8_general_ci
       CHECKSUM: NULL
 CREATE_OPTIONS: 
  TABLE_COMMENT: 
 row in set (0.00 sec)

可以看到,我们的行格式是dynamic,当然,索引的最大限制字节数就是3072了,我们再来看看当前索引的字节数:

col1---varchar(500)---utf8编码---1500字节

col2---varchar(500)---utf8编码---1500字节

col3---varchar(20)---utf8编码---60字节

col4---varchar(4)---utf8编码---12字节

可以看到,加起来一共就是3072字节。

因此当我们执行:

alter table t modify col4 varchar(5)的时候,索引idx_c1_c2_c3_c4就会超过3072字节,就会直接报错

如何解决这个更新问题?

既然我们无法修改c4这个列的varchar长度,那么我们如何实现我们修改字段内容的需求呢?

索引idx_c1_c2_c3_c4是为了保证唯一,其实对于col1这个字段来说,它的长度是500,其实没有必要把500个字段全部索引起来,只需要索引它的前若干个字符,例如前200个字符如果能保证唯一的区分度,那么索引前200个即可:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mysql> alter table t add UNIQUE KEY `idx_c1_c2_c3_c4` (`col1`(200),`col2`,`col3`,`col4`);
Query OK, 0 rows affected (0.07 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> show create table t\G
*************************** 1. row ***************************
       Table: t
Create Table: CREATE TABLE `t` (
  `id` int NOT NULL AUTO_INCREMENT,
  `col1` varchar(500) DEFAULT NULL,
  `col2` varchar(500) DEFAULT NULL,
  `col3` varchar(20) DEFAULT NULL,
  `col4` varchar(4) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_c1_c2_c3_c4` (`col1`(200),`col2`,`col3`,`col4`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql> 
mysql> alter table t modify col4 varchar(5);
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

通过这种办法,就能通过绕弯的方式实现索引区分的目的。

02

有Null值的列

首先创建一个包含default null字段的表:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mysql> show create table t1\G
*************************** 1. row ***************************
       Table: t1
Create Table: CREATE TABLE `t1` (
  `id` int NOT NULL,
  `name` mediumtext COLLATE utf8mb4_general_ci,
  `sex` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
1 row in set (0.00 sec)

mysql> select * from t1;
+----+------+------+
| id | name | sex  |
+----+------+------+
|  1 | aaa  | male |
|  2 | aaa  | male |
|  3 | aaa  | NULL |
+----+------+------+
3 rows in set (0.00 sec)

我们可以看到id=3的记录,sex列是NULL值,当我们对这个表进行修改,将sex列改成not null属性的时候:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mysql> alter table t1 modify sex varchar() COLLATE utf8mb4_general_ci NOT NULL;
Query OK,  rows affected,  warning (. sec)
Records:   Duplicates:   Warnings: 
## 查看下这个报警
mysql> show warnings;
+---------+------+------------------------------------------+
| Level   | Code | Message                                  |
+---------+------+------------------------------------------+
| Warning |  | Data truncated for column 'sex' at row 3 |
+---------+------+------------------------------------------+
 row in set (. sec)

mgrm5562:mysqlha_common@10.182.17. [test] 23:01:42> select * from t1;
+----+------+------+
| id | name | sex  |
+----+------+------+
|  1 | aaa  | male |
|  2 | aaa  | male |
|  3 | aaa  |      |
+----+------+------+
 rows in set (. sec)

可以看到一条警告,数据被truncate了,其实数据上发生的变化就是id=3的记录,NULL值被改成了空字符串。

这里的warning有一定的迷惑性。

这个例子告诉我们,当你的字段从default null属性改为not null属性的时候,出现的Data truncate操作不一定是数据被截断了,还有可能是数据从NULL值,被修改成了默认的空字符串。

总结

今天的文章,讲了2个小的例子,后续大家如果看到诸如此类的报错,希望可以有个灵感。

1、对于string类型,可以索引前几位来保证一定的区分度,又可以让索引的长度变小,索引的数据量也会对应变小;

2、default null字段的属性变为not null的时候,虽然报错Data truncate,但是你的数据没有丢失,只是将null值进行了修改

MySQL里面的细节非常多,时常留意,多积累,出现问题的时候才能游刃有余。

PS:文中的MySQL实验版本是5.7.24.

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-07-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DBA随笔 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
MySQL字段默认值设置详解
在 MySQL 中,我们可以为表字段设置默认值,在表中插入一条新记录时,如果没有为某个字段赋值,系统就会自动为这个字段插入默认值。关于默认值,有些知识还是需要了解的,本篇文章我们一起来学习下字段默认值相关知识。
MySQL技术
2021/03/04
11.3K0
CHAR与VARCHAR面面观
前面写过一篇介绍int类型的文章,一直想写一篇介绍字符串字段类型的文章,一直拖着也没思路要怎么下手。最近多关注了下这方面的文章,决定还是把拖了好久的文章了结了吧。本篇文章主要会介绍字符串类型char及varchar的用法及区别。
MySQL技术
2019/11/18
5560
INT类型知多少
整型是MySQL中最常用的字段类型之一,通常用于存储整数,其中int是整型中最常用的,对于int类型你是否真正了解呢?本文会带你熟悉int类型相关知识,也会介绍其他整型字段的使用。
MySQL技术
2019/09/08
9900
MySQL中dd::columns表结构转table过程以及应用
MySQL的dd表是用来存放表结构和各种建表信息的,客户端建的表都存在mysql.table和mysql.columns表里,还有一个表mysql.column_type_elements比较特殊,用来存放SET和ENUM类型的字段集合值信息。看一下下面这张表的mysql.columns表和mysql.column_type_elements信息。为了缩短显示长度,这里只展示几个重要的值。
GreatSQL社区
2023/02/23
8690
Mysql执行计划(大章)
使用explain关键字可以模拟优化器执行SQL查询语句,从而知道Mysql是如何处理你的SQL语句的。分析你的查询语句或者表结构的性能瓶颈
彼岸舞
2020/09/30
7910
MySQL 索引与性能调优
索引用于快速找出在某个列中有一特定值的行,如果不使用索引MySQL必须从第l条记录开始读完整个表,直到找出相关的行.表越大,查询数据所花费的时间越多,如果表中查询的列有一个索引,MySQL能快速到达某个位置去搜寻数据文件,而不必查看所有数据,可加快数据查询的查询速度提高效率,索引可在创建表时增加,也可动态调整已有表.
王 瑞
2022/12/28
2.4K0
MySQL 索引与性能调优
MySQL 案例:摸不准的查询优化器与索引
近期有用户在咨询查询的问题,发现一个比较典型的案例,SQL 语句无法选择正确的索引,导致查询效率偏低,正好借这个案例来学习一下查询优化器的知识。所使用的的 SQL 语句、数据以及表结构均已脱敏,
王文安@DBA
2022/02/28
1.1K0
MySQL 案例:摸不准的查询优化器与索引
MySQL的实战系列:大字段如何优化
除特别注明外,本站所有文章均为慕白博客原创,转载请注明出处来自https://geekmubai.com/programming/747.html
慕白
2018/09/21
5.4K0
MySQL的实战系列:大字段如何优化
MySQL 8.0新特性:函数索引
之前的文章中分别介绍了MySQL 8.0在索引方面的新特性--隐藏索引 和 降序索引,详细内容可查看之前的文章内容;
SEian.G
2021/04/15
1.2K0
【干货】MySQL索引与优化实践
索引的目的在于提高查询效率,其功能可类比字典,通过该索引可以查询到我们想要查询的信息,因此,选择建立好的索引十分重要
搜云库技术团队
2019/10/17
8860
创建索引,这些知识应该了解
在 MySQL 中,基本上每个表都会有索引,有时候也需要根据不同的业务场景添加不同的索引。索引的建立对于数据库高效运行是很重要的,本篇文章将介绍下创建索引相关知识及注意事项。
MySQL技术
2021/04/13
3170
Python操作MySQL数据库
在MYSQL中,运行INSERT INTO 插入语句中的字段是否一定要把该表的字段全部填上? 例如表tab_name有(col1,col2,col3,col4)4个字段。 只填你需要添的,就要把列
周小董
2019/03/25
1.7K0
MySQL 8.0 之不可见列
可以看到,我们的SQL里面创建了一个表t2的字段有id、name和age,其中,age字段设置了不可见属性。
AsiaYe
2021/06/09
1.6K0
MySQL 8.0 之不可见列
当谈 SQL 优化时谈些什么?
孙银行
2017/04/24
5.9K1
当谈 SQL 优化时谈些什么?
MySQL 执行计划深入解读
使用 EXPLAIN 关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是 如何处理你的SQL语句的,分析你的查询语句或是表结构的性能瓶颈。
没有故事的陈师傅
2021/11/15
7000
MySQL 执行计划深入解读
MySQL数据类型DECIMAL详解
当我们需要存储小数,并且有精度要求,比如存储金额时,通常会考虑使用DECIMAL字段类型,可能大部分同学只是对DECIMAL类型略有了解,其中的细节还不甚清楚,本篇文章将从零开始,为你讲述DECIMAL字段类型的使用场景及方法。
MySQL技术
2019/12/25
41.5K0
MySQL锁等待与死锁问题分析
在 MySQL 运维过程中,锁等待和死锁问题是令各位 DBA 及开发同学非常头痛的事。出现此类问题会造成业务回滚、卡顿等故障,特别是业务繁忙的系统,出现死锁问题后影响会更严重。本篇文章我们一起来学习下什么是锁等待及死锁,出现此类问题又应该如何分析处理呢?
MySQL技术
2021/04/13
2.1K3
mysql建立联合索引_mysql之联合索引
create index `sindex` on `test` (`aaa`,`bbb`,`ccc`);
全栈程序员站长
2022/09/06
5.4K0
你确定真正理解联合索引和最左前缀原则?
前文已经说了explain命令的大部分参数,接着图解: EXPLAIN 实战-1这篇文章把explain的key_len参数分享完,接着分享最左前缀原则,建立如下的表,其中name列和address列都建立了索引
Java识堂
2019/08/13
8320
MySQL客户端工具的使用与MySQL SQL语句
数据库配置文件为:/etc/my.cnf和/etc/my.cnf.d目录下的配置文件
Alone-林
2022/08/20
4.2K0
相关推荐
MySQL字段默认值设置详解
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验