再见MYSQL - 04 - Schema与数据类型优化

本文探讨了MySQL数据库中优化数据类型的策略,强调了选择适当数据类型的重要性,如整型、实数、字符串、日期时间、位数据类型等。优化包括选择更小的数据类型,减少NULL使用,以及利用枚举、CHAR和VARCHAR等特性。同时,文章讨论了MySQL Schema设计中的陷阱,如范式和反范式设计的优缺点,以及如何缓存和汇总数据以提升性能。此外,还介绍了加速ALTER TABLE操作的方法。
摘要由CSDN通过智能技术生成

好的 逻辑设计 与 物理设计 查询执行 是高性能的3架马车

4.1 选择优化的数据类型

数据类型越小越好
越简单越好
尽量 NOT NULL
NULL 对于索引,统计, 比较都会比较难, 尽量给 NOT NULL, 索引列的值 尽量不要有NULL出现

例如:
DATETIME 和 TIMESTAMP , 都可以表示时间,但是TIMESTAMP 只要一半的空间,并且根据时区变化,有特殊的自动更新能力

MYSQL为了兼容,有很多别名的数据类型,如 INTEGER, BOOL, NUMERIC, 实际上只是别名,不影响性能,使用SHOW CREATE TABLE会发现,其实还是基本类型

4.1.1整型

在这里插入图片描述
UNSIGNED 属性可以使数字的上限提高一倍,尽量UNSIGNED

整数计算使用的是 64位的 BIGINT, 即使 32 位环境也是如此

4.1.2 实数类型

可以用DECIMAL 存储比 BIGINT 还大的整数。
DECIMAL 用于精确的小数计算, 运算是由MYSQL 做的, 而FLOAT 由CPU直接运算,所以 FLOAT 的运行 会比 DECIMAL快一些。

FLOAT 4个字节, DOUBLE 8个字节 , 所以DOUBLE 有更大的精度和范围
在对小数进行精确计算时才用DECIMAL, 当数据量很大时,可以考试用BIGINT代替DECIMAL

4.1.3字符串

VARCHAR & CHAR

怎么存储的?

VARCHAR :

比定长更节省空间, 在UPDATE 时可能使行变得比原来更长,这就导致一些额外的工作.(更新长度数据)
如果使用ROW_FORMAT=FIXED, 则每行都会使用定长存储,有可能会浪费空间。
需要 1 ~ 2 个额外字节记录长度.

在这里插入图片描述
INNODB会把过长的VARCHAR 存为BLOB.

CHAR

当存储CHAR时,MYSQL会删除所有的末尾空格
适合存储长度差不多 的数据,如 MD5 值 . 定长的数据不容易产生碎片.
对于很短的数据 如只有 “Y” 和 “N” 的列. CHAR(1) 要1个字节, VARCHAR(1) 要2个字节, 因为还有一个字节要存长度.

如下图,VARCHAR 会保留所有的空格, CHAR 只会保留前面的空格
对于经常变更的数据,CHAR 也比 VARCHAR 好, 不容易产生碎片

字符串长度的定义是字符数不是字节数
在这里插入图片描述

在这里插入图片描述

BLOG & TEXT

BLOG: 二进制
TEXT: 字符串
mysql对他们排序逻辑不同,只对max_sort_length字节页不是整个字符串排序
如果只需要对一小部分排序,可以改 max_sort_length 的配置,或者使用 ORDER BY SUSTRING(column , length)

磁盘临时表,和文件排序 (filesort)

使用技巧: BLOG -> 字符串 SUBSTRING(column,length)

在这里插入图片描述

** 如果使用 EXPLAIN 执行计划的Extra 列包含“Using temporary” , 则説说明这个查询使用了 隐式临时表 **

使用枚举 (ENUM) 代替字符串类型

在这里插入图片描述

注意枚举类型排序 是按对应的数字,而不是字符串本身

在这里插入图片描述
可以使用FIELD()指定按内容排序, 但是会无法使用索引

在这里插入图片描述
当然,如果在录入时就是按照字母排序的,就不需要这样做了

枚举类型的最大弊端是只能在后面添加(mysql 5.1以后), 不然就只能alter table

4.1.4 日期和时间类型

MYSQL 能存储的最小时间粒度是秒,但是可以使用微秒级的粒度进进临时运算
MariaDB 最多是微秒

两种存储类型 DATETIME , TIMESTAMP

DATETIME
1001 - 9999, 精度为秒,格式YYYYMMDDHHMMSS整数, 与时区无关, 8个字节
默认情况下,格式如 “2009-01-02 21:22:12”

TIMESTAMP
保存从1970.01.01 (格林威治时间)零点开始, 与UNIX时间戳相同 4个字节
1970 ~ 2038年 注意 依赖于时区

TIMESTAMP 有DATETIME 没有的特殊属性: 如果插入时没有指定第一个TIMESTAMP的值,mysql可以设置这个列的值为当前时间
TIMESTAMP 默认是 ** NOT NULL **, 与其他数据库不同

在这里插入图片描述
如果没什么特殊要求,应该尽量使用TIMESTAMP, 不推荐整一个整数列来存TIMESTAMP

如果要使用小于秒的粒度的话,可以使用BIGINT存储微秒级别的时间戳,或者使用DOUBLE存储之后的小数部分, 或者使用MariaDB 替代 MYSQL

4.1.5 位数据类型

MYSQL 在底层存储 和 处理方式, 都是字符串类型

BIT

MYSQL 5.0 以前,BIT 就是 TINYINT, 5.0 以后,可以在BIT 列中存储一个或多个 true/false 值。
BIT(1)存一个包含单个位的字段 BIT(2)存储2个位, 最大长度是64 以此类推
MYISAM 会真的按位来存储, 而InnoDB会用最小整数来存放,所以不能节省存储空间。

MYSQL 把BIT当成字符串类型,也就是包含0 或 1 的字符串
在数字上下文中的检索时,又会把字符串转换成数字;
上下文不是数字时,又会按字符串处理
。如果 需要和另外 的值比较结果 。

注意如果不知道,这里会出现很多问题,如下内容,同样的内容,
字符串上下文是9 ,当数字上下文时又是 57

在这里插入图片描述

所以要尽量避免使用BIT 数据类型

SET

需要保存很多 true/false值

4.1.6 选择标识符 (identifier)

整数类型通常是最好的选择,因为他们很快并且可以使用自增

ENUM SET 通常不是好的选择, 对某些“静态”数据可能比较合适
比如可以设计一个枚举字段 对应产品类型,再对应一张以这个枚举字段为主键的查找表
在这里插入图片描述
尽量避免使用字符串作为标识列,因为他们比数字要大,也要慢 。尤其是MYISAM表

对于完全随机的字符串:
  1. 因为插入值会随机的写到索引的不同位置,所以使用INSERT语句更慢 。
    页分裂,磁盘随机访问,聚簇存储引擎

  2. SELECT 语句会变得更慢,因为逻辑上的相邻,而在磁盘上确有可能不在一坨存储的!

  3. 随机值 导致缓存对所有类型的查询 语句效果都很差, 访问局部性原理失效, 因为整个磁盘都是一样“热”

  4. 对应UUID, 应该移除“_”符号, 或者, 用UNHEX() 函数转换UUID为16字节的数字, 并且存储在一个BINARY(16)列中。 检索时可以用HEX() 函数来格式化为16进制格式。

  5. UUID 和SHA1() 不同,虽然分布不均匀,但是还是有一定顺序

自动生成的 schema

自动生成的 schema 可能会非常糟糕!

ORM ( 对象关系映射系统 )是一种常见的性能影响因素,也是一把双刃剑。

尽量在真实大小的数据集上做测试

4.1.7 特殊类型数据

有些并不直接与内置类型一致,
如低于秒级精度的时间戳
IPV4地址。 一般使用VARCHAR(15), 然后实际上是32位无符号整数
MYSQL 提供INET_ATON() 和 INET_NTOA() 函数在这两种表示 方法之间转换。

4.2 MySQL schema 设计中的陷阱

  • 太多的列, 实际上很少会用到。 这时MYSQL 从引擎层生成行缓冲格式的数据,然后在服务器层解码成各个列。
    这将会需要将编码过的列转换成行数据, 代价是很高的, 太多的列,太多的字段 会非常慢

  • 太多的关联 : EAV ( 实体 - 属性 - 值 )设计模板是一个常见的糟糕设计模式!尤其是mysql下不能靠谱的工作。
    MYSQL限制了每个关联操作最多只能有 61张表
    依经验 如果希望查询执行得快速且并发性好,单个查询最好在12个表以内做关联

  • 全能的枚举 : 防止过度使用枚举 (ENUM) 在mysql中,在枚举列表中增加一个新的国有就要做一次ALTER TABLE. 在这里插入图片描述

  • 变相的枚举

  • 非此发明的 NULL
    避免使用 NULL 好处多多,如果非要存储一个事实上的“空值”到表中, 也可以使用0, 某个特殊值,或者空字符串也行啊。
    但凡事不要走急端,如下图,构造一个全0值 可能导致很多问题, 或者用 -1 代表NULL, 也可能 导致代码中的各种问题
    在这里插入图片描述

4.3 范式 和 反范式

这里只先给出定义,具体可以参考相关资料

  1. 第一范式:每一列都是原子的,不能再分
  2. 第二范式:属性必须完全依赖于主键
    例如把这张表 在这里插入图片描述
    转化为下面两张表:
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200817163417961.png#pic_center在这里插入图片描述在这里插入图片描述

4.3.1 范式的优点和缺点

尤其是写密集的场景,通常建议对schema进行范式化设计

范式化的优点

  • 范式化的更新通常比反范式化要快
  • 范式化好,説明重复数据很少
  • 范式化表通常更不,可以更好的放进内存,所以更快
  • 很少的多余数据 意味着检索 列表数据时更少需要DISTINCT , GROUP BY
  • 在非范式化的结构中经常需要使用 DISTINCT GROUP BY 才能获得一份唯一的部门列表

范式化的缺点

  • 通常需要关联
  • 稍微复杂一些的查询 可能都需要至少一次关联 不但代价昂贵,也可能使一些索引策略无效

4.3.2 反范式的优点和缺点

优点

  • 因为数据都在一张表中,可以很好的避免关联。 最坏的情况就是没有索引 ,且对一张表全文索引,当数据比内存大时,这比关联要快得多,因为这样避免了随机I/O
    全表扫描基本上是顺序 I/O

随机I/O :数据布在整个磁盘,
顺序I/O :数据分布在一坨

4.3.3 混用范式化和反范式化

最常见的反范式化数据的方法是复制或者缓存, 在不同的表中存储相同的特定列。

在MYSQL 5.0 以后版本中, 可以使用触发器更新缓存值,这使得实现这样的方案更加简单。

另外从你表冗余一些数据到子表的理由是排序的需要。

缓存衍生值 比较要显示 一个用户发了多少消息,可以每次执行一个昂贵的查询 来获取,或者更好的,建一个 num_messages 列,每次发消息时更新这个值 。

4.4 缓存表 和 汇总表

汇总表 和 缓存表

有时提升性能最好的方法是在同一张表中保存衍生的冗余数据。 有时是需要创建一张完全独立的汇总表或缓存表。(特别是查询时)

在使用时,需要决定是实时维护数据还是定期更新。

影子表

当需要重建汇总表或缓存表 mb_statistics 时,先建一个影子表 mb_statistics_new, 在更新数据后再切换过去。 然后也可以将原来的 mb_statistics 命名为 mb_statistics_old,以做以后的处理

4.4.1 物化视图

开源工具 Flexviews 也可以实现物化视图, 可以不用重新查询 而更新视图。会替我们做几乎所有的脏活累活。

4.4.2 计算器表

简单的説就是把多个需要统计计数的数据都塞进一个表中,如点击次数,下载次数,脚本执行次数等。
为了提高并并量,可以把不同的计数数据放在多行中。

为了提升查询速度,经常会需要建一些额外索引,增加冗余列,甚至创建缓存表,汇总表, 写的操作变慢了, 读的速度变快了,同时开发难度也变大了

4.5 加速 ALTER TABLE

MYSQL 执行大部分修改表结构的操作是怎么运行的呢?

  1. 用新的结构创建一个新表
  2. 从旧表中查出所有数据,插入新表
  3. 删除旧表

这样的操作可能花费很长的时间,从数小时到数天都有可能

DDL & DML & DQL & DCL

SQL(Structure Query Language)语言是数据库的核心语言。

SQL语言共分为四大类:数据定义语言DDL,数据操纵语言DML,数据查询语言DQL,数据控制语言DCL。

  1. 数据定义语言DDL
    数据定义语言DDL用来创建数据库中的各种对象-----表、视图、索引、同义词、聚簇等如:
    CREATE TABLE/VIEW/INDEX/SYN/CLUSTER
    DDL操作是隐性提交的!不能rollback

2 .数据操纵语言DML
数据操纵语言DML主要有三种形式:

  1. 插入:INSERT
  2. 更新:UPDATE
  3. 删除:DELETE
  1. 数据查询语言DQL
    数据查询语言DQL基本结构是由SELECT子句,FROM子句,WHERE子句组成的查询块:
    SELECT <字段名表>
    FROM <表或视图名>
    WHERE <查询条件>

  2. 数据控制语言DCL
    数据控制语言DCL用来授予或回收访问数据库的某种特权,并控制数据库操纵事务发生的时间及效果,对数据库实行监视等。如:

  1. GRANT:授权。

  2. ROLLBACK [WORK] TO [SAVEPOINT]:回退到某一点。
    回滚—ROLLBACK
    回滚命令使数据库状态回到上次最后提交的状态。其格式为:
    SQL>ROLLBACK

一般而言 , ALTER TABLE 会导致 MYSQL 服务中断。有两个方法可以有效处理:

  1. 在一台空闲的服务器上执行ALTER TABLE, 然后和提供服务的主库进行切换
  2. 影子拷贝: 用要求的表结构创建一个和源表无关的新表,然后重命名,删除操作交换两张表
  3. 下面的工具也可以实现无锁的表结构变更 facebook: online schema change 工具, openark toolkit, percona toolkit

不是所有 ALTER TABLE 都会重建表,
例如 要改变一个列的默认值 :

方法1: 慢 因为这会新建一张表,进行拷贝和删除.
所有的 MODIFY COLUMN 操作都会导致表的重建
在这里插入图片描述
方法2 :快, 只改.frm文件
ALTER COLUMN
在这里插入图片描述

ALTER TABLE 允许使用 ALTER COLUMN, MODIFY COLUMN, CHANGE COLUMN 修改列。这三个语句的运行方式都是不一样的。

4.5.1 只修改 .frm文件 (非官方方法 操作之前请先备份!!!)

不需要重建表的操作:

在这里插入图片描述
骚操作如下 :
在这里插入图片描述
例:

第一步,建新结构的空表,
注意新的值要放在后面
在这里插入图片描述

第二步,交换 ***.frm 文件

在这里插入图片描述

第三步 删除辅助表

在这里插入图片描述

4.5.2 快速创建 MyISAM 索引

高效的载入数据到MYISAM 中,常用的技巧是先禁用索引 ,载入数据, 然后再启用索引

在这里插入图片描述
此法对唯一索引 无效

在InnoDB 版本中, 有一个类似的技巧, 这依赖于InnoDB 的快速在线索引 创建功能
具体过程是 : 先删除所有的非唯一索引 ,然后增加新的列, 最后重新创建删除掉的索引。

Percona Server 可以自动完成这些操作步骤:
在这里插入图片描述在这里插入图片描述
这个操作对大表来说会快很多

4.6 总结

MYSQL 喜欢 小而简单

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值