mysql的这几个坑你踩过没?真是防不胜防

本文涉及的产品
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: mysql的这几个坑你踩过没?真是防不胜防

前言


对于从事互联网开发的同学来说,mysql可谓是再熟悉不过的了。无论是DBA、开发或测试,基本上天天要跟它打交道,很多同学可能已经身经百战了。但是,笔者遇到过的这些坑不知道你们都经历过没?


正文


有符号和无符号


以前我们公司在项目开发之初制定开发规范时,对mysql的int类型字段定义成有符号,还是无符号问题专门讨论过。


观点一:


对于能够确定里面存的值一定是正数的字段,定义成UNSIGNED无符号的,可以节省一半的存储空间。创建无符号字段的语句如下:


create table test_unsigned(a int UNSIGNED, b int UNSIGNED);


观点二:


建议都定义成有符号的,使用起来比较简单,mysql默认int类型就是有符号的,创建有符号字段的语句如下:


create table test_signed(a int);
insert into test_signed values(-1);


执行结果:

9.png

在字段a中插入-1,我们看到是可以操作成功的。


这两个方案,经过我们激烈讨论之后,选择了使用有符号定义int类型字段。为什么呢?


create table test_unsigned(a int UNSIGNED, b int UNSIGNED);
insert into test_unsigned values(1,2);

先创建test_unsigned表,里面包含两个无符号字段a和b,再插入一条数据a=1,b=2


    select b - a from test_unsigned;


    没有问题,返回1

    但是如果sql改成这样:


    select a - b from test_unsigned;


    执行结果:


    8.png


    报错了。。。


    所以,在使用无符号字段时,千万要注意字段相减出现负数的坑,建议还是使用有符号字段,避免不必要的问题。


    自动增长


    建过表的同学都知道,对于表的主键可以定义成自动增长的,这样一来,就可以交给数据库自己生成主键值,而无需在代码中指定,而且生成的值是递增的。一般情况下,创建表的语句如下:


    create table test_auto_increment (a int auto_increment primary key);

    但如果改成这样的会怎样?


    create table test_auto_increment (a int auto_increment);


    执行结果:


    00.png


    报错了。。。

    截图中没有全部显示出来,完整的提示语是这样的:


    1075 - Incorrect table definition; there can be only one auto column and it must be defined as a key, Time: 0.006000


    意思是自动增长字段,必须被定义成key,所以我们需要加上primary key。

    此外,还有一个有趣的实验:


    insert into test_auto_increment(a) values (null),(50),(null),(8),(null);


    大家猜猜执行结果会是什么样的?

    11.png

    第一个null插入1,然后按真实的数字大小排序后插入,后面两个null,是在最大的数字上面加1。

    再看看这条sql主键中插入负数,能执行成功吗?


    insert into test_auto_increment values(-3);


    答案是可以,主键可以插入负数。

    13.png


    还有这条sql呢,主键中插入0?


    insert into test_auto_increment values(0);


    执行结果:


    12.png

    也可以执行成功,但是没有插入数据


    字段长度


    我们在创建表的时候,给字段定义完类型之后,紧接着需要指定字段的长度,比如:varchar(20),biginit(20)等。那么问题来了,varchar代表的是字节长度,还是字符长度呢?


    create table test_varchar(a varchar(20));
    insert into test_varchar values('苏三说技术');
    select length(a),CHARACTER_LENGTH(a) from test_varchar;

    执行后的结果:

    15.jpg

    我们看到中文的5个字length函数统计后长度为15,代表占用了15个字节,而使用charcter_length函数统计长度是5,代表有5个字符。所以varchar代表的是字符长度,因为有些复杂的字符或者中文,一个字节表示不了,utf8编码格式的一个中文汉字占用3个字节。不同的数据库编码格式,占用不同的字节数对照表如下:

    14.png


    mysql除了varchar和char是代表字符长度之外,其余的类型都是代表字节长度。

    int(n) 这个n表示什么意思呢?从一个列子出发:

    create table test_bigint (a bigint(4) ZEROFILL);
    insert into test_bigint values(1);
    insert into test_bigint values(123456);
    select * from test_bigint;

    ZEROFILL表示长度不够填充0

    执行结果:

    17.png

    mysql常用数字类型字段占用字节数对照表:

    16.png

    从表中可以看出bigint实际长度是8个字节,但是我们定义的a显示4个字节,所以在不满4个字节时前面填充0。满了4个字节时,按照实际的长度显示,比如:123456。但是,需要注意的是,有些mysql客户端即使满了4个字节,也可能只显示4个字节的内容,比如显示:1234。

    所以bigint(4),这里的4表示显示的长度为4个字节,实际长度还是8个字节。


    忽略大小写


    我们知道在英文字母中有大小写问题,比如:a 和 A 是一样的吗?我们认为肯定是不一样的,但是数据库是如何处理的呢?

    create table test_a(a varchar(20));
    insert into test_a values('a');
    insert into test_a values('A');
    select * from test_a where a = 'a';


    执行结果是什么呢?

    18.png

    本以为只会返回a,但是实际上把A也返回了,这是为什么呢?

    24.png


    该表默认的Collation是utf8_general_ci,这种Collation会忽略大小写,所以才会出现查询小写字母a的值,意外把大写字母A的值也查询出来了。


    那么如果我们只想查询出小写a的值该怎么办?先看看mysql支持哪些Collation?


    show collation;


    22.png


    从上图中我们可以找到utf8_bin,这个表示二进制格式的数据,我们设置成种类型的试试。

    21.png

    修改一下字段类型


    ALTER TABLE test_a MODIFY COLUMN a VARCHAR(20) BINARY CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL;

    再查看一下数据


    select * from test_a where a = 'a';


    执行结果:


    20.png

    果然,结果对了。


    特殊字符


    笔者之前做项目的时候,提供过一个留言的功能,结果客户端用户输入了一个emoji表情,直接导致接口报错了。

    26.png

    最后定位原因是由于当时数据库和表的字符编码都是用的utf8,mysql的utf8编码的一个字符最多3个字节,但是一个emoji表情为4个字节,所以utf8不支持存储emoji表情。

    该如何解决这个问题呢?

    将字符编码改成utf8mb4,utf8mb4最多能有4字节,不过,在mysql5.5.3或更高的版本才支持。

    在mysql 的配置文件 my.cnf 或 my.ini 配置文件中修改如下:


    [client]
    default-character-set = utf8mb4
    [mysqld]
    character-set-server = utf8mb4
    collation-server     = utf8mb4_general_ci


    重启MySQL,然后使用以下命令查看编码,应该全部为utf8mb4,这是修改整个数据库的编码方式。


    SHOW VARIABLES WHERE Variable_name LIKE 'character_set_%' OR Variable_name LIKE 'collation%';ji

    结果为:

    23.png


    也可以单独修改某张表的编码方式:


    alter table test_a convert to character set utf8mb4 collate utf8mb4_bin;


    以及修改某个字段的编码方式:


    ALTER TABLE test_a CHANGE a a VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;


    此外,建议同学们在创建数据库和表的时候字符编码都定义成utf8mb4,避免一些不必要的问题。

    相关实践学习
    如何在云端创建MySQL数据库
    开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
    全面了解阿里云能为你做什么
    阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
    相关文章
    |
    2月前
    |
    SQL 网络协议 关系型数据库
    【怒怼大厂面试官】听说你精通MySQL?来说说MySQL主从复制
    面试官:MySQL主从复制了解吧?嗯嗯了解的。主要是利用了MySQL的Binary Log二进制文件。那我把二进制文件丢给从库,从库复制整个文件吗。噢噢不是的。
    62 1
    【怒怼大厂面试官】听说你精通MySQL?来说说MySQL主从复制
    |
    11月前
    |
    存储 SQL 关系型数据库
    技术同学必会的MySQL设计规约,都是惨痛的教训
    怎么才能很好的避免低级故障?以下规范在大型互联网公司经过了充分的验证,尤其适用于并发量大、数据量大的业务场景。
    33683 22
    |
    SQL Oracle 关系型数据库
    期末mysql复习枯燥,乏味.一文带你轻松击破mysql壁垒.
    期末mysql复习枯燥,乏味.一文带你轻松击破mysql壁垒.
    131 0
    查漏补缺第四期(Mysql相关)
    前言 目前正在出一个查漏补缺专题系列教程, 篇幅会较多, 喜欢的话,给个关注❤️ ~ 本专题主要以Java语言为主, 好了, 废话不多说直接开整吧~ 项目有没有用到sql优化,你是如何优化的呢? 使用索引:索引是数据库中提供快速访问数据的重要工具。确保在查询中使用到的列上创建索引,特别是经常用于过滤、排序和连接的列。然而,过多的索引也会导致性能下降,因此需要权衡选择合适的列创建索引。
    |
    SQL 数据采集 编解码
    想不到吧,Mysql在项目中的优化场景这么多
    想不到吧,Mysql在项目中的优化场景这么多
    228 0
    想不到吧,Mysql在项目中的优化场景这么多
    |
    存储 SQL 安全
    猿创征文|深聊MySQL,从入门到入坟之:应该是全网最详细的MySQL知识点汇总,必须收藏。(三)
    猿创征文|深聊MySQL,从入门到入坟之:应该是全网最详细的MySQL知识点汇总,必须收藏。(三)
    115 1
    |
    关系型数据库 MySQL 数据库
    猿创征文|深聊MySQL,从入门到入坟之:应该是全网最详细的MySQL知识点汇总,必须收藏。(一)
    猿创征文|深聊MySQL,从入门到入坟之:应该是全网最详细的MySQL知识点汇总,必须收藏。(一)
    111 1
    |
    SQL 关系型数据库 MySQL
    深聊MySQL,从入门到入坟之:MySQL竟然也有后悔药!!!
    深聊MySQL,从入门到入坟之:MySQL竟然也有后悔药!!!
    65 0
    |
    关系型数据库 MySQL
    猿创征文|深聊MySQL,从入门到入坟之:应该是全网最详细的MySQL知识点汇总,必须收藏。(二)
    猿创征文|深聊MySQL,从入门到入坟之:应该是全网最详细的MySQL知识点汇总,必须收藏。(二)
    84 0
    |
    SQL 关系型数据库 MySQL
    删库不跑路:我含泪写下了 MySQL 数据恢复大法…(3)
    删库不跑路:我含泪写下了 MySQL 数据恢复大法…(3)
    203 0