1. 记住几条简单的原则
- 更小的通常更好
尽量使用正确存储数据的最小数据类型。更小的数据类型通常更快,因为他们占用更小的磁盘,内存和CPU缓存,并且处理时需要的CPU周期也更少。 简单就好
简单数据类型的操作通常需要更少的CPU周期。- 例如,整型比字符操作代价更低,因为字符集和校对规则使字符比较复杂与整型比较。
- 例如,使用Mysql的內建类型而不是字符串来存储日期和时间。
- 例如:用整型存储IP地址。
尽量避免NuLL
通常情况下最好指定列为NOT NULL,除非真的需要存储NULL值。如果查询中包含可为NULL的列,不利于MYSQL的优化(索引管理因为NULL变得复杂)。
但是通常把NULL的列改为NOT NULL的性能提升比较小,如果计划在列上建索引,尽量避免设计可为NULL的列。
2. 基础数据类型详解
1. 整数类型
TINTINT | SMALLINT | MEDIUMINT | INT | BIGINT |
---|---|---|---|---|
8 | 16 | 24 | 32 | 64 |
- 以上为存储整数的可用类型以及相应的存储位数, 范围为-2^(n-1)到2^(n-1)-1,n为存储位数。
- 整数类型的无符号UNSIGNED属性是可选的,表示不可为负,可以使正数的上限提高一倍。但是有符号和无符号使用相同的存储空间,根据实际情况选择。
- MYSQL可以为整数类型指定宽度,例如INT(11),但是对于存储和计算的性能来说,INT(1)和INT(20)没有区别,只有显示字符个数之间的差别。
2. 实数类型
- 主要分为两类:浮点数和DECIMAL,都支持指定精度。
- 浮点类型FLOAT和DOUBLE分别占4字节和8字节,MYSQL使用DOUBLE作为内部浮点计算的类型。
- DECIMAL计算开销大,尽量避免使用。
3. 字符串类型
VRCHAR
- 存储可变长字符串,比定长节省空间,对性能也有帮助。
- 需要使用1个或2个字节记录字符串长度,列长度小于等于255,则只用1个字节记录长度,否则是两个;
- 因为行可能边长,在UPDATE时若空间增长,可能需要额外的工作(页分裂),容易产生碎片。
- 推荐使用VARCHAR的情况:
- 字符串序列的最大长度比平均长度大很多。
- 列更新比较少
- 使用了UTF-8这样复杂的字符集,每个字符都使用不同的字节数存储。
CHAR
- 定长,适合存储很短的字符串,或者字符串所有值都接近同一个长度。
- 对于经常更新的数据,CHAR更合适,不容易产生碎片,虽然更占用空间。
- 跟VARCHAR比,CHAR不需要额外记录长度,所以非常短的列适合用CHAR。
- 对于CHAR来说,如果长度不足的话,存储时MYSQL会用空格补全,检索时自动剔除末尾空格;对于VARCHAR来说,MYSQL5.0及更高版本会保留末尾空格,而老版本不会。
BINARY和VARBINARY
- 存储二进制字符串。二进制字符串存储的是字节码,采用\0填充
- BINARY和VARBINARY适合用来存储二进制数据,二进制比较 比普通字符串的比较的效率要高。
BIOB和TEXT
- 都为存储很大的数据而设计的,BOLB采用二进制存储,TEXT采用字符方式存储。
补充:使用枚举(ENUM)代替字符串
- 如果字符串的值固定几种的话,适合用枚举代替字符串
- 枚举内部保存为整数,能够节省空间,提高性能。
4. 日期和时间类型
- DATETIME:把日期时间封装到格式为‘YYYYMMDDHHMMSS’的整数中,精确到秒,使用8字节存储,与时区无关。
- TIMESTAMP:保存了1970年1月1日午夜(格林尼治标准时间)到现在的秒数的秒数,使用4个字节存储,只能表示1970年到2038年,显示的值依赖时区,TIMESTAMP列默认为NOT NULL。
- 推荐使用TIMESTAMP,因为它用整数类存储,方便计算机计算,空间效率高。
- DATE:3字节,日期,格式为YYYYMMDD
- YEAR:1字节,年份,格式为YYYY
- TIME:3字节,时间,格式为HHMMSS
5. 位数据类型
- BIT: 内部使用整数存储,但是实际情况又比较复杂,不推荐使用。
- SET:用一系列打包好的位的集合来表示,缺点是列定义的代价高(需要用ALTER TABLE),并且在SET列无法使用索引
- SET使用案例:保存基于权限的控制访问表:
标识列数据类型的选择
- 最好选择是整数类型,速度快,并且可以AUTO_INCREMENT
- 避免使用字符串做标识,消耗空间,比整数慢。
- 随机字符串(例如UUID()产生)会任意分布在很大的空间内,导致INSERT和SELECT变慢。
3. MYSQL数据库设计的常见问题
1. 太多的列
- MYSQL的服务器和存储引擎之间通过行缓存格式拷贝数据,然后在服务器层将缓冲内容解码成各个列。从行缓冲中将编码后的列转换成行数据结构的操作代价是十分高的,转换的代价取决于列的数量。
2. 太多的关联
- MYSQL限制了关联操作最多只能有61张表,如果希望查询执行快而且并发性能好,单个查询的表关联数目最好在12个以内。
3. 过度使用枚举
- 导致设计凌乱;增加一次枚举就要ALTER TABLE,代价可能很高。
4. 范式与反范式的选择
范式的优点与缺点
优点:
- 范式的更新操作比反范式快
- 数据冗余少
- 范式化的表更小,可以更好的放在内存里,执行操作更快。
- 重复数据少意味着更少使用DISTINCT以及GROUP BY语句
缺点:
- 通常需要关联,关联可能使一些索引无效。
反范式的优点与缺点
优点:
- 数据集中在一张表,避免关联
- 查询比关联快,避免了随机I/O
- 单独的表能更有效的使用索引策略
最佳选择:混用范式化和反范式化
- 完全的范式化和反范式化都不可取
- 推荐采用部分范式化,复制或者缓存,在不同表中存储相同的特定列,允许部分数据冗余,利用冗余做更高效率的查询
实例:
- 对于需要统计数量的情况,可以增加一个字段来存储数量并实时更新,避免昂贵的统计查询。
- 缓存表与汇总表:建立一张简单的表保存衍生的数据。
- 计数器表:解决并发问题。例如统计网站点击数量:
CREATE TABLE hit_counter(
slot tinyint unsigned not null primary key,
cnt int unsigned not null
)ENGINE=InnoDB;
- 然后预先在这张表增加一百行数据,网站被点击一次时,选择一个随机的槽(slot)进行更新
UPDATE hit_counter SET cnt = cnt + 1 WHERE slot = RAND()*100;
- 要获得统计结果,需要这样查询
SELECT SUM(cnt) FROM hit_counter
参考 :《高性能Mysql》