作者 | 草捏子

来源 | 草捏子(ID:chaycao)

头图 |  CSDN 下载自东方IC

本文就数据类型和schema方面的优化进行介绍。

选择优化的数据类型

MySQL支持的数据类型有很多,而如何选择出正确的数据类型,对于性能是至关重要的。以下几个原则能够帮助确定数据类型:

  1. 更小的通常更好

    应尽可能使用可以正确存储数据的最小数据类型,够用就好。这样将占用更少的磁盘、内存和缓存,而在处理时也会耗时更少。

  2. 简单就好

    当两种数据类型都能胜任一个字段的存储工作时,选择简单的那一方,往往是最好的选择。例如整型和字符串,由于整型的操作代价要小于字符,所以当在两者之间选择时,选择整型通常能够获得更好的性能。

  3. 尽量避免NULL

    当列可为NULL时,对于MySQL来说,在索引和值比较等方面需要做更多的工作,虽然对性能的影响不是很大,但也应尽量避免设计为可为NULL。

除了以上原则,在选择数据类型时,需遵循的步骤:首先确定合适的大类型,例如数据、字符串、时间等;然后再选择具体的类型。下面将讨论大类型下的一些具体类型,首先是数字,有两种类型:整数和实数。

一、整数类型

整数类型和所占用的空间如下:

整数类型 空间大小(bit)
TINYINT 8
SMALLINT 16
MEDIUMINT 24
INT 32
BIGINT 64

整数类型所能存储的范围和空间大小有关:-2^(N-1)至2^(N-1)-1,其中N为空间大小的位数。

整数类型具有UNSIGNED的可选属性,当声明时,表示不允许负数,则存储范围变为:0至2^(N)-1,扩大了一倍。

在MySQL中,还可以为整数类型指定宽度,例如INT(1),但这样的意义并不大,并不会限制值的合法范围,仍能存储-2^31至2^31-1的值,所影响的是与MySQL的交互工具显示字符的个数。

二、实数类型

实数类型的对比如下:

实数类型 空间大小(Byte) 取值范围 计算精度
FLOAT 4 负数:-3.4E+38~-1.17E-38;非负数:0、1.17E-38~3.4E+38 近似计算
DOUBLE 8 负数:-1.79E+308~-2.22E-308;非负数:0、2.22E-308~1.79E+308 近似计算
DECIMAL 与精度有关 同DOUBLE 精确计算

从上面可以看出,FLOAT和DOUBLE都有固定的空间大小,但同时由于是使用标准的浮点运算,所以只能近似计算。而DECIMAL则可以实现精确计算,与此同时占用的空间会相较更大,所耗费的计算开销也更多。

DECIMAL所占空间大小与指定的精度有关,例如DECIMAL(M,D):

  • M为整个数字的最大长度,取值范围为[1, 65],默认值为10;

  • D为小数点后的长度,取值范围为[0, 30],且D <= M,默认值为0。

MySQL在存储DECIMAL类型时会作为二进制字符串存储,每4个字节存9个数字,当不足9位时,数字的占用空间如下:

数字个数 占用空间(Byte)
1、2 1
3、4 2
5、6 3
7、8 4

小数点前后将分别存储,同时小数点也要占1个字节。下面举两个计算的例子:

  1. DECIMAL(18, 9):整数部分长度为9,占用4个字节。小数部分长度为9,占用4个字节。同时加上小数点1个字节,则总共占用9个字节。

  2. DECIMAL(20, 9):整数部分长度为14,占用7(4+3)个字节。小数部分长度为9,占用4个字节。同时加上小数点1个字节,则总共占用12个字节。

可以看出DECIMAL的空间占用还是很大的,因此只有当需要对小数进行精确计算时,才需要使用DECIMAL。除此之外,我们还可以使用BIGINT代替DECIMAL,例如需要保证小数点后5位的计算,可以将值乘上10的5次方后作为BIGINT存储,这样能同时避免浮点存储计算不精确和DECIMAL精确计算代价高的问题。

三、字符串类型

最常用的字符串类型当属 VARCHAR 和 CHAR。VARCHAR 作为可变长字符串,会使用1或2个额外字节记录字符串的长度,当最大长度未超过255时,只需1个字节记录长度,超过255,则需2个字节。VARCHAR 的适用场景

  1. 最大长度比平均长度大很多;

  2. 列的更新少,避免碎片;

  3. 使用复杂的字符集,如UTF-8,每个字符能使用不同的字节存储。

CHAR 则为定长字符串,根据定义的字符串长度分配足够的空间,适用场景

  1. 长度短;

  2. 长度相近,例如MD5;

  3. 经常更新。

除了 VARCHAR 和 CHAR,针对存储大字符串,可以使用 BLOB 和 TEXT 类型。BLOB 和 TEXT 的区别在于,BLOB 是以二进制方式存储,而 TEXT 是以字符方式存储。这也导致,BLOB 类型的数据没有字符集的概念,无法按字符排序,而 TEXT 类型则有字符集的概念,可以按字符排序。两者的使用场景,也由存储格式决定了,当存储二进制数据时,例如图片,应使用 BLOB,而存储文本时,例如文章,则应使用TEXT类型。

四、日期和时间类型

MySQL中所能存储的最小时间粒度为秒,常用的日期类型有DATETIME和TIMESTAMP。

类型 存储内容 空间大小(Byte) 时区概念
DATETIME 格式为YYYYMMDDHHMMSS的整数 8
TIMESTAMP 从1970年1月1日零点以来的秒数 4

TIMESTAMP显示的值将依赖于时区,意味在不同时区查询到的值将不一样。除了以上列出的不同,TIMESTAMP还具有一个特殊属性,在插入和更新时,如果没有指定第一个TIMESTAMP列的值,将会设置这个列的值为当前时间。

我们在开发过程中,应尽量使用TIMESTAMP,主要是因为其空间大小仅需DATETIME的一半,空间效率更高。

如果我们想存储的日期和时间精确到秒之后,怎么办?由于MySQL并未提供,所以我们可以使用BIGINT存储微妙级别的时间戳,或者使用DOUBLE存储秒之后的小数部分。

五、选择标识符

通常来说整数是标识符的最好选择,主要是因为其简单,计算快,且可使用AUTO_INCREMENT。

范式和反范式

简单来说,范式就是一张数据表的表结构所符合的某种设计标准的级别。第一范式,属性不可分割,现在的RDBMS系统建成的表都是符合第一范式的。而第二范式,则是消除非主属性对码(可以理解为主键)的部分依赖。第三范式消除非主属性对码的传递依赖。具体的介绍,可以读读知乎上的这个回答(https://www.zhihu.com/question/24696366/answer/29189700)

严格范式化的数据库中,每个事实数据会出现且只出现一次,不会出现数据冗余,这样所能带能带来的好处有:

  1. 更新操作更快;

  2. 修改更少的数据;

  3. 表更小,更好地放内存中,执行操作更快;

  4. 更少需要 DISTINCT 或 GROUP BY。

但也由于数据分散存在各张表中,查询时需要对表进行关联。而反范式的优点则是不用进行关联,将数据冗余存储。

在实际应用中,不会出现完全的范式化或完全的反范式化,时常需要混用范式和反范式,使用部分范式化的schema,往往是最好的选择。关于数据库设计,在网上看到这样一段话,大家可以感受下。

数据库设计应该分为三个境界:

第一境界:刚入门数据库设计,范式的重要性还未深刻理解。这时候出现的反范式设计,一般会出问题。

第二境界:随着遇到问题解决问题,渐渐了解到范式的真正好处,从而能快速设计出低冗余、高效率的数据库。

第三境界:再经过N年的锻炼,是一定会发觉范式的局限性的。此时再去打破范式,设计更合理的反范式部分。

范式就像武侠里面的招数,初学者妄想不按招数来,只能死的很难堪。毕竟招数都是高手总结归纳的精华。而随着武功提高,招数熟练之后,必然是发现招数的局限性,要么忘掉招数,要么自创招数。

只要努力,加上多熬几年,总能达到第二个境界,总会觉得范式是经典。此时能不过分依赖范式,快速突破范式局限性的人,自然是高手。

缓存表和汇总表

除了上述说到的反范式,在表中存储冗余数据,我们还可以创建一张完全独立的汇总表或缓存表,来满足检索的需要。

缓存表,指的是存储可以从schema其他表中获取数据的表,也就是逻辑上冗余的数据。而汇总表,则指的是存储使用GROUP BY等语句聚合数据,计算出的不冗余的数据。

缓存表,可用于优化搜索和检索查询语句,这里可以使用的技巧有对缓存表使用不同的存储引擎,例如主表使用InnoDB,而缓存表则可使用MyISAM,获得更小的索引占用空间。甚至可以将缓存表放到专门的搜索系统中,例如Lucene。

汇总表,则是为了避免实时计算统计值所带来的高昂代价,代价来自两方面,一是需要扫描表中的大部分数据,二是建立特定的索引,会对UPDATE操作有影响。例如,查询微信过去24小时的朋友圈数量,则可固定每1小时扫描全表,统计后写一条记录到汇总表,当查询时,只需查询汇总表上最新的24条记录,而不必每次查询时都去扫描全表进行统计。

在使用缓存表和汇总表时,必须决定是实时维护数据还是定期重建,这取决于我们的需求。定期重建相比实时维护,能节省更多的资源,表的碎片更少。而在重建时,我们仍需保证数据在操作时可用,需要通过“影子表”来实现。在真实表后创建一张影子表,当填充好数据后,通过原子的重命名操作来切换影子表和原表。

加快ALTER TABLE操作的速度

当 MySQL 在执行 ALTER TABLE 操作时,往往是新建一张表,然后把数据从旧表查出并插入到新表中,再删除旧表,如果表很大,这样需要花费很长时间,且会导致 MySQL 的服务中断。为了避免服务中断,通常可以使用两种技巧

  1. 在一台不提供服务的机器上执行 ALTER TABLE 操作,然后再与提供服务的主库进行切换;

  2. “影子拷贝”,建立一张与原表无关的新表,在数据迁移完成后,通过重命名操作进行切换。

但也不是所有的 ALTER TABLE 操作会引起表重建,例如在修改字段的默认值时,使用 MODIFY COLUMN 会进行表重建,而使用 ALTER COLUMN 则不会进行表重建,操作速度很快。这是因为 ALTER COLUMN 在修改默认值时,会直接修改了存在表的.frm文件(存储字段的默认值),而并未重建表。

参考

  1. 《高性能MySQL》

  2. MySQL DECIMAL 数据类型(https://my.oschina.net/u/559356/blog/3057960)

更多精彩推荐
☞机器人也开始"怕疼"了?科学家开发无需人工干预即可"自愈"的机器人☞对话指令集创始人兼CEO潘爱民:面向未来的新型物联网操作系统 | 人物志
☞@程序员,什么才是“2020-1024”的正确打开姿势?☞最新!百度首发 OCR 自训练平台 EasyDL OCR☞SQL分页查询方案的性能对比
☞2021年,很可能是以太坊的“高光之年”
点分享点点赞点在看

学习MySQL,怎么能不会数据类型和schema优化!相关推荐

  1. MySQL调优(二):数据类型和schema优化,MySQL8.0取消查询缓存的原因

    数据类型和schema优化 数据类型的优化 合理使用范式和反范式 三大范式: 1.表不可分 2.不能存在传递依赖 3.表里其他列的值必须唯一依赖于主键 约定大于规范,没有必要严格遵守范式,以业务为准, ...

  2. MySQL(二)数据类型和schema优化

    数据类型和schema优化 数据类型的优化 更小的通常更好 <高性能MySQL> 应该尽量使用可以正确存储数据的最小数据类型,更小的数据类型通常更快,因为它们占用更少的磁盘.内存和CPU缓 ...

  3. mysql 如何对表排序_学习MySQL:对表中的数据进行排序和过滤

    mysql 如何对表排序 In this article, we will learn how we can sort and filter data using the WHERE clause a ...

  4. linux 进程间通信 dbus-glib【实例】详解三 数据类型和dteeth(类型签名type域)(层级结构:服务Service --> Node(对象、object) 等 )(附代码)

    linux 进程间通信 dbus-glib[实例]详解一(附代码)(d-feet工具使用) linux 进程间通信 dbus-glib[实例]详解二(上) 消息和消息总线(附代码) linux 进程间 ...

  5. php中自动转换、强制转换、其他数据类型和bool转换

    0x01 自动转换 运算过程需要的数据类型和提供的数据类型不一致,将数据类型转为自己需要的类型 <?phpheader('content-type:text/html;charset=utf-8 ...

  6. Python数据类型、Numpy数据类型和Pytorch中的tensor类型间的相互转化

    数据类型包括Python数据类型.Numpy数据类型和Pytorch中的tensor,Pytorch中的tensor又包括CPU上的数据类型和GPU上的数据类型. 一.Python数据类型 Pytho ...

  7. 金仓数据库KingbaseES数据类型和oracle数据类型的映射表

    关键字:kingbase,oracle,数据类型 KingbaseES数据类型和oracle数据类型的映射表如下: Oracle数据类型 KingbaseES 数据类型 备注说明(Oracle) 数值 ...

  8. 常用数据类型和bytes数组互转

    常用数据类型和bytes数组互转 简介 常用于tcp和udp传输,通过约定协议进行数据交互 示例 1.在Python中一般用struct库进行常见的转换,主要对数字进行处理,包括int.float等 ...

  9. MySQL数据类型和Java数据类型对应关系表

    MySql 数据类型和 Java 数据类型之间的转换是很灵活的. 一般来讲,任何 MySql 数据类型都可以被转换为一个 java.lang.String,任何 MySql 数字类型都可以被转换为任何 ...

最新文章

  1. 一文读懂人工智能产业最新发展趋势
  2. 计算天数java_Java,计算两个日期之间的天数
  3. 欧拉函数的一道练习题(附加容斥做法)
  4. CSS3制作文字背景图
  5. IOS学习笔记之十一(包装类、description、isequal)
  6. SpringBoot中mybatis配置多数据源
  7. 判断给定数组是否包含132模式 132 Pattern
  8. maven spring hibernate shiro
  9. ElementUI下拉框选择后不显示值
  10. Atitit 图像处理和计算机视觉的分类 三部分 图像处理 图像分析 计算机视觉
  11. 09-JS的事件流的概念(重点)
  12. python基于大数据的招聘信息实时数据分析系统的设计与实现
  13. MySQL可视化软件(Navicat)部署与使用
  14. 深度学习中的一些概率函数分布
  15. 【Proteus仿真】74LS138译码器流水灯
  16. 欧姆龙HOSTLINK协议,上位机软件欧姆龙HOSTLINK协议,适合欧姆龙全系列PLC
  17. The Security Database on the Server Does Not Have a Computer Account
  18. 基于 SpringBoot 和 webSocket 的匿名聊天室
  19. 解决使用Edge(必应)浏览器搜索之后显示untitled的问题
  20. python爬虫系列——开始入土(二)数据解析

热门文章

  1. css中选择器的使用
  2. Java 学习笔记(9)——java常用类
  3. 文本输入框input将输入转换为统一大小写
  4. iOS中将后台JSON数据转化为模型的总结
  5. [C++] pair
  6. leetcode 700 二叉搜索树的搜索
  7. 如何在矩池云上查看cudnn版本
  8. 矩池云上编译安装dlib库
  9. pandas作图_pandas绘图
  10. 数据结构与算法——链表汇总整理