整数类型:
TINYINT,SWALLINT, MEDIUMINT,INT,BIGINT. 分别是 8,16,24,32,64 位储存空间。
整数型有 UNSIGNED 属性,例如 TINYINT. UNSIGNRD 可以储存的范围是 0~255,TINYINT 储存范围是 -128~127. 储存的空间是相同的。
使用最适合的类型。
实数类型:
FLOAT,DOUBLE,DECIMAL. 都可以指定精度,例如 DECIMAL(18,9) 小数两边各存储9个数字,一共9个字节,小数点前用 4 个字节,小数点后 4 个,小数点占一个。
DECIMAL 允许最多 64 个数字。计算的时候会转换为 DOUNBLE 类型。
FLOAT 使用 4 个字节存储。DOUBLE 占用 8 个字节。
尽量只在对小数进行精确计算的时候才使用 DECIMAL。在数据量比较大的时候,可以考虑使用 BIGINT 代替 DECIMAL。
字符串类型
VARCHAR 和 CHAR 是主要的字符串类型。
VARCHAR 类型用于储存可变长字符串,需要使用 1 或者 2 个额外字节记录长度,小于等于 255 字节用一个,大于用两个。字符串列的最大长度比平均长度大很多;列的更新少,所以碎片不是问题;使用了像 UTF-8 这样复杂的字符集,每个字符都使用不同的字节数存储。
CHAR是定长的,适合存储比较短的字符串,或者所有值都接近一个长度。非常短的列,单字符集。
与 CHAR 和 VARCHAR 类似的类型还有 BINARY 和 VARBINARY。它们储存的是二进制字符串,它的储存是字节码不是字符,填充是 \0 不是空格。优势是大小写敏感,比较式每次按一个字节,比较快。
BLOB 和 TEXT 是为了存储很大的数据而设计的字符串类型。
字符类型 TINTTEXT,SMALLTEXT,TEXT,MEDIUNTEXT,LONGTEXT;二进制类型 TINYBLOB,SMALLBLOB,BLOB,MEDIUMBLOB,LONGNBLOB。
BLOB 和 TEXT值太大的时候,InnoDB 会使用专门的“外部”存储区来进行存储,此时每个值在行内需要 1~4 个字节存储一个指针,外部存储实际的值。
BLOB 和 TEXT之间仅有的不同是 BLOB 是二进制数据,没有排序柜子或字符集,而 TEXT 类型有字符集和排序规则。
使用枚举代替字符串类型
枚举不好的地方是:字符串列表是固定的,添加删除字符串必须使用 ALTER TABLE。 对于一系列未来可能会改变的字符串,使用枚举不是好主意。
日期和时间类型
DATATIME
从 1001 到 9999年 精度为秒。YYYYMMDDHHMMSS
TIMESTAMP
1970到2038年 只使用 4 个字节。
尽量使用 TIMESTAMP。
位数据类型
BIT
可以使用 BIT 列在一列中存储一个或者多个 true/false 值。最大长度是 64 个位。当检索 BIT(1)的时候,结果是一个包含二进制 0 或 1 值的字符串,而不是 ASCII 码的 “0” “1”。然而在数字上下文场景的时候结果是将位字符串转换为数字。
SET
如果要保存很多的 true/false 可以考虑合并这些列到一个 SET 。主要缺点,改变列的定义需要 ALTER TABLE。一般也无法在 SET 列上通过索引查找。
一种替代方式,使用一个整数包装一系列的位。每一位代表一个 0/1。
选择标识符
为标识列选择合适的数据类型非常重要,一般来说跟更有可能用标识列与其他值进行比较,标识列可能在另外的表作为外键使用,所以为标识列选择数据类型的时候,应该选择跟关联表中的对应列一样的类型。
整数通常是最好的选择,ENUM SET适合存储固定信息,避免使用字符串类型。因为一些 SELECT 语句变的很慢。
特殊类型数据
例如 IPv4 地址,经常用 VARCHAR(15) 来储存 IP 地址。是 32 位无符号整数,用小数点分为四段方便阅读。不是字符串,MySQL 提供 INET_ATON() 和 INET_NTOA()函数在这两种表示方法之间转换。
MySQL schema 设计中的陷阱
太多的列
MySQL 的储存引擎 API 工作时需要在服务器和储存引擎之间通过行缓存格式拷贝数据,然后在服务器层将缓冲内容解码成各个列。说白了列越多转换代价就会非常高。
太多的关联
实体-属性-值(EVA) 是常见的糟糕的设计模式,MySQL限制了每个关联操作最多只能有61张表,EVA 需要许多自关联。经验告诉我们,单个查询最好在12个表以内做关联。
全能的枚举
防止过度使用枚举,如果枚举值过多,应当用整数作为外键关联到字典表或者查找表来查找具体值。
但是每增加一个枚举值有需要 ALTER TABLE。
范式的优点和缺点
范式化的更新操作通常比反范式化要快。
当数据较好地范式化时,就只有很少没有重复的数据,所以只需要修改更少的数据。
范式化的表通常更小,可以更好的放在内存里,所以执行操作会更快。
很少有多余的数据意味着检索列表数据时更少需要 DISTINCT 或者 GROUP BY 语句。
缺点是通常需要关联。
反范式的优点和缺点
很好地避免了关联。
在实际应用当中都是混用范式化和反范式化的。
最常见的反范式化的数据方法是复制或者缓存,在不同的表中存储相同的特定列,可以使用触发器更新缓存值。
网站实例中,可以再 user 和 message 中都储存 account_type 字段,避免了完全反范式化的插入和删除问题,不会吧 user_message 表搞的太大,有利于高效的获取数据。但是更新用户的账户类型操作代价就高了,这需要考虑更新的频率以及更新的时长,并和执行 SELECT 查询的频率进行比较。
如果需要显示每个用户发了多少消息,可以再 user 表中建一个 num_message 列,每当用户发消息就更新这个表。
缓存表和汇总表
有时提升性能最好的方法是在同一张表中保存衍生冗余数据,有时也需要创建一张完全独立的汇总表和缓存表。
缓存表储存那些可以比较简单地从 schema 其他表(但每次获取的速度比较慢)数据的表
汇总表保存的是使用 GROUP BY 语句聚合数据的表。 缓存表对优化搜索和检索查询语句很有效。
当重建汇总表和缓存表示需要一个影子表。
加快 ALTER TABLE 操作的速度
对于常见的场景,只有两种:一是先在一台不提供服务的机器上执行 ALTER TALBLE 操作,然后和提供服务的主库进行切换。另一种是,影子拷贝,是用要求的表结构创建一张和源表不管的新表,然后通过重命名和删除操作叫唤两张表。
一些场景中,可以通过 ALTER COLUMN 操作来改变列的值,这样只会修改.frm文件。