MySQL 那些常见的错误设计规范,你都知道吗

依托于互联网的发达,我们可以随时随地利用一些等车或坐地铁的碎片时间学习以及了解资讯。同时发达的互联网也方便人们能够快速分享自己的知识,与相同爱好和需求的朋友们一起共同讨论。

但是过于方便的分享也让知识变得五花八门,很容易让人接收到错误的信息。这些错误最多的都是因为技术发展迅速,而且没有空闲时间去及时更新已经发布的内容所导致。为了避免给后面学习的人造成误解,我们今天来看一看 MySQL 设计规范中几个常见的错误例子。

主键的设计

错误的设计规范:主键建议使用自增 ID 值,不要使用 UUID,MD5,HASH,字符串作为主键

这个设计规范在很多文章中都能看到,自增主键的优点有占用空间小,有序,使用起来简单等优点。

下面先来看看自增主键的缺点:

  • 自增值由于在服务器端产生,需要有一把自增的 AI 锁保护,若这时有大量的插入请求,就可能存在自增引起的性能瓶颈,所以存在并发性能问题;
  • 自增值做主键,只能在当前实例中保证唯一,不能保证全局唯一,这就导致无法在分布式架构中使用;
  • 公开数据值,容易引发安全问题,如果我们的商品 ID 是自增主键的话,用户可以通过修改 ID 值来获取商品,严重的情况下可以知道我们数据库中一共存了多少商品。
  • MGR(MySQL Group Replication) 可能引起的性能问题;

因为自增值是在 MySQL 服务端产生的值,需要有一把自增的 AI 锁保护,若这时有大量的插入请求,就可能存在自增引起的性能瓶颈。比如在 MySQL 数据库中,参数 innodb_autoinc_lock_mode 用于控制自增锁持有的时间。虽然,我们可以调整参数 innodb_autoinc_lock_mode 获得自增的最大性能,但是由于其还存在其它问题。因此,在并发场景中,更推荐 UUID 做主键或业务自定义生成主键。

我们可以直接在 MySQ L使用 UUID() 函数来获取 UUID 的值。

MySQL> select UUID();
+--------------------------------------+
| UUID()                               |
+--------------------------------------+
| 23ebaa88-ce89-11eb-b431-0242ac110002 |
+--------------------------------------+
1 row in set (0.00 sec)

需要特别注意的是,在存储时间时,UUID 是根据时间位逆序存储, 也就是低时间低位存放在最前面,高时间位在最后,即 UUID 的前 4 个字节会随着时间的变化而不断“随机”变化,并非单调递增。而非随机值在插入时会产生离散 IO,从而产生性能瓶颈。这也是 UUID 对比自增值最大的弊端。

为了解决这个问题,MySQL 8.0 推出了函数 UUID_TO_BIN,它可以把 UUID 字符串:

  • 通过参数将时间高位放在最前,解决了 UUID 插入时乱序问题;
  • 去掉了无用的字符串"-",精简存储空间;
  • 将字符串其转换为二进制值存储,空间最终从之前的 36 个字节缩短为了 16 字节。

下面我们将之前的 UUID 字符串 23ebaa88-ce89-11eb-b431-0242ac110002 通过函数 UUID_TO_BIN 进行转换,得到二进制值如下所示:

MySQL> SELECT UUID_TO_BIN('23ebaa88-ce89-11eb-b431-0242ac110002',TRUE) as UUID_BIN;
+------------------------------------+
| UUID_BIN                           |
+------------------------------------+
| 0x11EBCE8923EBAA88B4310242AC110002 |
+------------------------------------+
1 row in set (0.01 sec)

除此之外,MySQL 8.0 也提供了函数 BIN_TO_UUID,支持将二进制值反转为 UUID 字符串。

虽然 MySQL 8.0 版本之前没有函数 UUID_TO_BIN/BIN_TO_UUID,还是可以通过自定义函数的方式解决。应用层的话可以根据自己的编程语言编写相应的函数。

当然,很多同学也担心 UUID 的性能和存储占用的空间问题,这里我也做了相关的插入性能测试,结果如下表所示:

可以看到,MySQL 8.0 提供的排序 UUID 性能最好,甚至比自增 ID 还要好。此外,由于 UUID_TO_BIN 转换为的结果是16 字节,仅比自增 ID 增加 8 个字节,最后存储占用的空间也仅比自增大了 3G。

而且由于 UUID 能保证全局唯一,因此使用 UUID 的收益远远大于自增 ID。可能你已经习惯了用自增做主键,但是在并发场景下,更推荐 UUID 这样的全局唯一值做主键。

当然了,UUID虽好,但是在分布式场景下,主键还需要加入一些额外的信息,这样才能保证后续二级索引的查询效率,推荐根据业务自定义生成主键。但是在并发量和数据量没那么大的情况下,还是推荐使用自增 UUID 的。大家更不要以为 UUID 不能当主键了。

金融字段的设计

错误的设计规范:同财务相关的金额类数据必须使用 decimal 类型 由于 float 和 double 都是非精准的浮点数类型,而 decimal 是精准的浮点数类型。所以一般在设计用户余额,商品价格等金融类字段一般都是使用 decimal 类型,可以精确到分。

但是在海量互联网业务的设计标准中,并不推荐用 DECIMAL 类型,而是更推荐将 DECIMAL 转化为整型类型。 也就是说,金融类型更推荐使用用分单位存储,而不是用元单位存储。如1元在数据库中用整型类型 100 存储。

下面是 bigint 类型的优点:

  • decimal 是通过二进制实现的一种编码方式,计算效率不如 bigint
  • 使用 bigint 的话,字段是定长字段,存储高效,而 decimal 根据定义的宽度决定,在数据设计中,定长存储性能更好
  • 使用 bigint 存储分为单位的金额,也可以存储千兆级别的金额,完全够用

枚举字段的使用

错误的设计规范:避免使用 ENUM 类型

在以前开发项目中,遇到用户性别,商品是否上架,评论是否隐藏等字段的时候,都是简单的将字段设计为 tinyint,然后在字段里备注 0 为什么状态,1 为什么状态。

这样设计的问题也比较明显:

  • 表达不清:这个表可能是其他同事设计的,你印象不是特别深的话,每次都需要去看字段注释,甚至有时候在编码的时候需要去数据库确认字段含义
  • 脏数据:虽然在应用层可以通过代码限制插入的数值,但是还是可以通过sql和可视化工具修改值

这种固定选项值的字段,推荐使用 ENUM 枚举字符串类型,外加 SQL_MODE 的严格模式

在MySQL 8.0.16 以后的版本,可以直接使用check约束机制,不需要使用enum枚举字段类型

而且我们一般在定义枚举值的时候使用"Y","N"等单个字符,并不会占用很多空间。但是如果选项值不固定的情况,随着业务发展可能会增加,才不推荐使用枚举字段。

索引个数限制

错误的设计规范:限制每张表上的索引数量,一张表的索引不能超过 5 个

MySQL 单表的索引没有个数限制,业务查询有具体需要,创建即可,不要迷信个数限制

子查询的使用

错误的设计规范:避免使用子查询

其实这个规范对老版本的 MySQL 来说是对的,因为之前版本的 MySQL 数据库对子查询优化有限,所以很多 OLTP 业务场合下,我们都要求在线业务尽可能不用子查询。

然而,MySQL 8.0 版本中,子查询的优化得到大幅提升,所以在新版本的MySQL中可以放心的使用子查询。

子查询相比 JOIN 更易于人类理解,比如我们现在想查看2020年没有发过文章的同学的数量

SELECT COUNT(*)
FROM user
WHERE id not in (
    SELECT user_id
    from blog
    where publish_time >= "2020-01-01" AND  publish_time <= "2020-12-31"
)

可以看到,子查询的逻辑非常清晰:通过 not IN 查询文章表的用户有哪些。

如果用 left join 写

SELECT count(*)
FROM user LEFT JOIN blog
ON user.id = blog.user_id and blog.publish_time >= "2020-01-01" and blog.publish_time <= "2020-12-31"
where blog.user_id is NULL;

可以发现,虽然 LEFT JOIN 也能完成上述需求,但不容易理解。

我们使用 explain查看两条 sql 的执行计划,发现都是一样的

通过上图可以很明显看到,不论是子查询还是 LEFT JOIN,最终都被转换成了left hash Join,所以上述两条 SQL 的执行时间是一样的。即,在 MySQL 8.0 中,优化器会自动地将 IN 子查询优化,优化为最佳的 JOIN 执行计划,这样一来,会显著的提升性能。

总结

阅读完前面的内容相信大家对 MySQL 已经有了新的认知,这些常见的错误可以总结为以下几点:

  • UUID 也可以当主键,自增 UUID 比自增主键性能更好,多占用的空间也可忽略不计
  • 金融字段除了 decimal,也可以试试 bigint,存储分为单位的数据
  • 对于固定选项值的字段,MySQL8 以前推荐使用枚举字段,MySQL8 以后使用check函数约束,不要使用 0,1,2 表示
  • 一张表的索引个数并没有限制不能超过5个,可以根据业务情况添加和删除
  • MySQL8 对子查询有了优化,可以放心使用。

到此这篇关于MySQL 那些常见的错误设计规范的文章就介绍到这了,更多相关MySQL 错误设计规范内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • MYSQL 数据库命名与设计规范

    1.设计原则 1) 标准化和规范化 数据的标准化有助于消除数据库中的数据冗余.标准化有好几种形式,但Third Normal Form(3NF)通常被认为在性能.扩展性和数据完整性方面达到了最好平衡.简单来说,遵守3NF 标准的数据库的表设计原则是:"One Fact in One Place"即某个表只包括其本身基本的属性,当不是它们本身所具有的属性时需进行分解.表之间的关系通过外键相连接.它具有以下特点:有一组表专门存放通过键连接起来的关联数据. 举例:某个存放客户及其有关定单的3

  • 专业级的MySQL开发设计规范及SQL编写规范

    在团队开发过程中为了项目的稳定,代码的高效,管理的便捷制定内部种开发设计规范是必不可少的, 这里分享一份我们定义MySQL开发设计规范包括表设计规范,字段设计规范,SQL编写规范 数据库对象命名规范 数据库对象 命名规范的对象是指数据库SCHEMA.表TABLE.索引INDEX.约束CONSTRAINTS等的命名约定 数据库对象命名原则 命名使用具有意义的英文词汇,词汇中间以下划线分隔 命名只能使用英文字母.数字.下划线 避免用MySQL的保留字如:call.group等 所有数据库对象使用小写

  • MySQL 那些常见的错误设计规范,你都知道吗

    依托于互联网的发达,我们可以随时随地利用一些等车或坐地铁的碎片时间学习以及了解资讯.同时发达的互联网也方便人们能够快速分享自己的知识,与相同爱好和需求的朋友们一起共同讨论. 但是过于方便的分享也让知识变得五花八门,很容易让人接收到错误的信息.这些错误最多的都是因为技术发展迅速,而且没有空闲时间去及时更新已经发布的内容所导致.为了避免给后面学习的人造成误解,我们今天来看一看 MySQL 设计规范中几个常见的错误例子. 主键的设计 错误的设计规范:主键建议使用自增 ID 值,不要使用 UUID,MD

  • MySQL中常见的八种SQL错误用法示例

    前言 MySQL在2016年仍然保持强劲的数据库流行度增长趋势.越来越多的客户将自己的应用建立在MySQL数据库之上,甚至是从Oracle迁移到MySQL上来.但也存在部分客户在使用MySQL数据库的过程中遇到一些比如响应时间慢,CPU打满等情况. 阿里云RDS专家服务团队帮助云上客户解决过很多紧急问题.现将<ApsaraDB专家诊断报告>中出现的部分常见SQL问题总结如下,供大家参考. 1.LIMIT 语句 分页查询是最常用的场景之一,但也通常也是最容易出问题的地方. 比如对于下面简单的语句

  • mysql数据库常见基本操作实例分析【创建、查看、修改及删除数据库】

    本文实例讲述了mysql数据库常见基本操作.分享给大家供大家参考,具体如下: 本节相关: 创建数据库 查看数据库 修改数据库 删除数据库 首发时间:2018-02-13 20:47 修改: 2018-04-07:考虑到规范化,将所有语法中"关键字"变成大写;以及因为整理"mysql学习之路",移除字符集和校对集问题并归成一个新博文. 创建数据库  : 语法  : CREATE DATABASE 数据库名字[库选项]; 库选项说明  : 库选项是可选项,可以不写 ,如

  • MySQL中常见的几种日志汇总

    前言: 在 MySQL 系统中,有着诸多不同类型的日志.各种日志都有着自己的用途,通过分析日志,我们可以优化数据库性能,排除故障,甚至能够还原数据.这些不同类型的日志有助于我们更清晰的了解数据库,在日常学习及运维过程中也会和这些日志打交道.本节内容将带你了解 MySQL 数据库中几种常用日志的作用及管理方法. 1.错误日志(errorlog) 错误日志记录着 mysqld 启动和停止,以及服务器在运行过程中发生的错误及警告相关信息.当数据库意外宕机或发生其他错误时,我们应该去排查错误日志. lo

  • MySQL中常见的六个约束类型详解

    目录 前言 1. not null 2. unique 3. default 4. primary key 自增主键auto_increment 5. foreign key 6. check 总结 前言 在向数据表中插入数据时,有的时候对于插入的数据是有特殊要求的,比如学生成绩不能为空,学生学号不能重复等等,这个时候约束就派上用场了. 约束类型是通过对表的行或列的数据做出限制,来确保表的数据的完整性.唯一性.这一章我们主要介绍六个常见的MySQL约束类型. 1. not null 保证指定不能

  • VC6.0常见编译错误提示附解决方法

    (1)error C2001: newline in constant 编号:C2001直译:在常量中出现了换行.错误分析: 1.①字符串常量.字符常量中是否有换行.2.②在这句语句中,某个字符串常量的尾部是否漏掉了双引号.3.③在这语句中,某个字符创常量中是否出现了双引号字符""",但是没有使用转义符"\"".4.④在这句语句中,某个字符常量的尾部是否漏掉了单引号.5.⑤是否在某句语句的尾部,或语句的中间误输入了一个单引号或双引号. (2)er

  • 详谈Linux开发中常见段错误问题的原因及分析

    1    使用非法的内存地址(指针),包括使用未经初始化及已经释放的指针.不存在的地址.受系统保护的地址,只读的地址等,这一类也是最常见和最好解决的段错误问题,使用GDB print一下即可知道原因. 2    内存读/写越界.包括数组访问越界,或在使用一些写内存的函数时,长度指定不正确或者这些函数本身不能指定长度,典型的函数有strcpy(strncpy),sprintf(snprint)等等. 3    对于C++对象,应该通过相应类的接口来去内存进行操作,禁止通过其返回的指针对内存进行写操

  • DOS命令行下常见的错误信息

    大家在使用DOS的过程中,经常在命令行方式下遇到一些错误信息提示,由于往往是英文的,导致一些人看到后不知是怎么回事,更不知该如何解决了.下面,我就将常见的DOS命令行方式下的错误信息向大家介绍一下. [英文] Bad command or file name  [译文] 错误的命令或文件名 错误原因和解决: 这大概是大家最常见到的错误提示了,它的意思是输入的命令无效.当输入的命令既不是DOS内部命令,而且系统在查找路径或指定路径中找不到相应的可执行文件的话,就会出现此错误信息.您可以检查输入的命

  • SQL Server 完整备份遇到的一个不常见的错误及解决方法

    1. 错误详情 有一次在手动执行数据库完整备份时遇到如下错误: 执行多次都是这个错误信息. 提示无法生成检查点,原因可能是由于系统资源(如磁盘或内存空间)不足或者有时是由于数据库损坏而造成的. 我们检查数据库资源可以排除磁盘资源不足的情况. 2.检查点相关知识 事务日志.数据文件 和checkpoint的关系. 在SQL Server中,进行insert, update, delete时,数据并没有直接写入数据库对应的mdf文件中,而是写入了缓存里,这时,就要提到一个非常重要机制:CheckPo

  • 10种JavaScript最常见的错误(小结)

    前言 查看了数千个项目后,发现了 10 个最常见的 JavaScript 错误.我们会告诉你什么原因导致了这些错误,以及如何防止这些错误发生.如果你能够避免落入这些 "陷阱",你将会成为一个更好的开发者. JavaScript 常见错误 Top 10: 为了便于阅读,我们将每个错误描述都尽量简化.接下来,让我们深入到每一个错误,来了解是什么会导致它,以及如何避免这个问题. 1.Uncaught TypeError: Cannot read property 如果你是一个 JavaScr

随机推荐