原则

  1. 更小的通常更好

    • 更小的数据类型,占据更小的磁盘,内存和CPU缓存,并且处理时需要的CPU周期也更少
    • 需要确定没有低估需要存储的值得范围
  2. 简单就好

    • 简单数据类型的操作需要更少的CPU周期
    • 整型比字符串操作低
  3. 尽量避免null
    • 为null 的列使得索引,索引统计和值比较都更复杂
    • 可为null的列会使用更多的存储空间,在mysql里需要额外处理
    • 当可以为null的列被索引时,每个索引记录需要一个额外的值
    • 甚至会导致,固定大小的索引,变成可变大小的索引
    • Innodb 有单独的位bit 来存储null 所以一般这种调优 提升比较小 其实

选择调优的数据类型

整数类型

有这几种整数类型,

  • tinyint 8位存储空间
  • smllint 16位
  • mediumint 24位
  • int 32位
  • bigint 64位

他们的值可以从image.png N 为位数
整数类型 有 正数和负数 unsigned 属性 表示不允许为负数, 如果不为负数,那么取值范围 上限提高一倍, 如果原来是 -10 - 10 如果不为负数 那么就可以为 0 - 20

int(1) 和 int(20) 对于存储和计算来说 是一样的

实数类型

实数就是带有小数的数字

  • float 和 double 类型支持使用标准的浮点运算进行近似计算
  • decimal 可以存储比bigint 还要大的数字 用于存储精确地小数 只有需要精确计算的时候才使用
  • 数量比较大时,可以使用bigint代替,例如可以将小数字 乘以100万 然后存储起来

字符串类型

varchar 和 char 类型

varchar
  • 用于存储可变长度的字符串,比定长更节省空间
  • 由于时变长的,所以在update时候更耗费时间
  • 适合的场景
    • 字符串列的最大长度比平均长度大很多
    • 列的更新很少,所以碎片不是问题
    • 使用了像UTF-8这样复杂的字符集,每个都是用不同的字节数进行存储
  • 大于等于5.0的版本 存储和检索的时候 会保留末尾空格

char
  • 存储char值时,会删除结尾空格
  • 适合的场景
    • 存储密码的MD5值,因为MD5时定长的

最好的策略就是 只分配真正需要的空间

binary 和 varbinary类型

他们存储的是二进制字符串,存储的是字节码 而不是字符

blob 和 text 类型

都是为了存储很大的数据而设计的字符串数据类型

  • blob 采用二进制存储
  • text 采用字符存储

text

字符串类型分别为

  • tinytext
  • smalltext
  • text
  • mediumtext
  • longtext

blob

二进制类型分别为

  • tinyblob
  • smallblob
  • blob
  • mediumblob
  • longblob

当blob 和 text 过大的时候,存储引擎会使用 专门的 外部的存储区域来存储
此时在行内存储的是 1~ 4字节大小的指针 类似于 堆内存 在栈内存中存储的一个指针

使用枚举代替 字符串类型

对于一些固定值列,可以使用枚举

日期 和 时间类型

mysql 能存储的最小时间粒度为秒
大部分时间类型都没有替代品,因此没有最佳选择

datetime 和 timestamp

datetime

  • 存储 1001 - 9999 年 范围之间的值, 精确度为秒 封装到 YYYY-MM-DD HH-MM-SS 的格式中, 与时区无关
  • 使用8个字节存储
  • 保留文本表示的日期和时间

timestamp

除了特殊情况,我们就尽量使用timestamp,因为效率高

  • 保存了 从190.1.1 开始 的秒数
  • 只是用4个字节的存储空间
  • 范围比datetime小 只能 到2038年
  • FROM_UNIXTIME 方法可以吧时间戳转为 日期
  • UNIX_TIMESTAMP 方法把日期 转为时间戳
  • 与时区有关,时间会与时区的变化而变化
  • 默认为not null
  • 插入值时,如果没有指定列的值,那么就会将列设置为当前时间 自动设置的 所以一般记录此行数据的插入时间

位数据类型

少数的几种类型用位存储数据
不管存储方式和处理方式,从技术上来说,位数据类型 其实都属于字符串类型

bit

谨慎使用

  • 在一列中存储一个或者多个 true/false 值
  • 最大长度为64位
  • InnoDB 使用一个足够存储的最小类型来存放,所以不能节省空间

选择标识符

特殊类型数据

例如 ipv4 地址 其实是一个32位 无符号整数 不是字符串
所以应该使用无符号整数来存储ip地址,
mysql提供了 inet_aton() 和 inet_ntoa() 方法来在这两种数据之间转换

MySQL schema 设计中的缺陷

  1. 太多的列
  • 存储引擎API 在工作的时候需要在服务器层和存储引擎层之间通过行缓冲格式,拷贝数据,然后再服务层将缓冲内容解码成各个列,从行缓冲中将编码 过得列转为行数据结构是非常耗性能的,所以一般要求列的数量不要太多了
  1. 太多的关联
  • EVA 实体 属性 值 模式 单个查询关联的表最好少于12个表
  1. 全能的枚举
  • 防止过度使用枚举
  1. 变相的枚举
  2. 非此发明的null

范式和反范式

范式

第一范式

表中的每一列,都是最小的数据单元,确保不可再分,则满足第一范式

例如:user用户表,包含字段id,username,password

第二范式

在第一范式的基础上,保证每一列的数据都和主键有关

例如:一个用户只有一种角色,而一个角色对应多个用户。则可以按如下方式建立数据表关系,使其满足第二范式。
user用户表,字段id,username,password,role_id
role角色表,字段id,name

第三范式

在第二范式的基础上,更近一步,表的每一列与主键都强相关,所以需要将关系列单独拆分出去成一个表

例如:一个用户可以对应多个角色,一个角色也可以对应多个用户。则可以按如下方式建立数据表关系,使其满足第三范式。

user用户表,字段id,username,password

role角色表,字段id,name

user_role用户-角色中间表,id,user_id,role_id

像这样,通过第三张表(中间表)来建立用户表和角色表之间的关系,同时又符合范式化的原则,就可以称为第三范式。

反范式