第三章 shcema 与数据类型优化

良好的逻辑设计和物理设计是高性能的基石。

一、选择优化的数据类型
 mysql支持的数据类型很多,选择正确的数据类型对于获取高性能至关重要,几个选择数据类型的原则:

     1.更小的通常更好:一般情况下,尽量选择可以存储正确数据的最小数据类型。更小的数据类型占据更小的磁盘、内存和CPU缓存。
    
    2.简单就好:简单数据类型操作通常需要更小的CPU周期,例如,整型比字符串操作代价更低。

    3.尽量避免NULL:通常情况下最好指定列为NOT NULL,除非真的需要保存NULL。如果查询包含null的列,对mysql来说更难优化,因为可为null的列使索引,索引统计和值比较都更复杂

整数类型:TINYINT|SMALLINT|MEDIUMINT|INT|BIGINT 分别是8、16、24、32、64为存储空间,可以存储的值范围为-2的(n-1)次方到2的(n-1)次方,其中N为存储空间,整数类型有UNSIGNED可选属性,表示不允许负值,这大致可以使
    正数上线提高一倍,比如TINYINT范围-128~127,加上UNSIGNED后则是255。整数计算一般使用64位的BIGINT,MYSQL可为整数指定宽度,例如INT(11),它不会限制值的合法范围,只是规定了MYSQL的交互工具,
    用来显示字符串的个数。但是对于存储来说,INT(1)和INT(11)是相同的

实数类型:实数是带有小数部分的数字,然而,它们不只是为了存储小数部分,也可以使用DECIMAL存储比BIGINT还大的整数。MYSQL既支持精确类型,也支持不精确类型。FLOAT和DOUBLE类型支持使用标准的浮点运算进行近似运算。
    DECIMAL类型用于存储精确的小数,MYSQL5.0以上,支持精确计算。浮点类型通常比DECIMAL使用更少的空间,所有尽量只在对小数进行精确计算时才使用DECIMAL,在数据量比较大的时候,可以考虑用BIGINT分存储货币,以
    解决浮点型不精确和DECIMAL计算代价高的问题。

字符串类型:
    VARCHAR 和 CHAR类型:VARCHAR类型用于存储可变长字符串,它比定长类型更节省空间,因为它只使用必要的空间;CHAR类型的是定长的,MYSQL总是根据定义的字符串长度分配足够的空间,CHAR值适合存储很短的字符串,
    或者所有值都接近同一个长度,对于经常需要变更的值,CHAR也比VARCHAR更好,对于非常短的列,CHAR也比VARCHAR更有效率。

    tips:使用VARCHAR(5)和VARCHAR(100) 存储"hello"的空间是一样的,但是更长的列会消耗更多的内存,因为MYSQL通常会分配固定大小的内存来保存内存值,最好的策略是只分配真正需要的空间

BLOB和TEXT类型
    BLOB和TEXT都是为了存储很大的数据而设计的字符串数据类型,分别采用二进制和字符方式存储。与其它类型不同,MYSQL把每个BOLO和TEXT值当作一个独立对象处理,存储引擎在存储的时候通常会做特殊处理,当BOLO和TEXT
    值太大时,InnoDB会使用专门的‘外部’存储区域进行存储,在每个值的行内存储一个指针指向外部存储的实际的值。BLOB和TEXT区别是,BOLB存储二进制,没有排序规则或字符集,而TEXT有字符集和排序。

日期和时间类型:MYSQL可以使用多种类型来保存时间或者日期。
    DATETIME:这个类型能保存最大范围的值,从1111~9999,精度为秒,它把日期和时间封装到格式为YYYYMMDDHHMMSS的整数中,与时区无关,使用8个字节存储空间。
    TIMESTAMP:这个类型保存了从1970-01-01以来的秒数,他与UNIX时间戳相同,TIMESTAMP只使用4个字节存储空间,因此它的范围比DATETIME小得多,只能表示1970-2038年。
    TIMESTAMP和DATETIME很不一样,前者提供的值与时区相关,后者则保留文本表示日期时间,默认情况下,如果插入时没有指定第一个TIMESTAMP列的值,MYSQL会设置当前时间为这个列的值。TIMESTAMP列默认NOT NULL,这也和
    DATETIME不同。

选择标识列:关联操作中,通过标识列查找其他列,或者标识列作为其它表的外键使用,这时要保证关联表中的列数据类型完全一致,包括像UNSIGNED这样的属性。混用可能导致性能问题,或者隐式转换错误。
    标识列小技巧:1.整数类型时标识列的最好选择,因为他们很快且可以使用AUTO_INCREMENT
              2.SET和ENUM是标识列的糟糕选择
              3.字符串类型:如果可能,应该避免字符串类型作为表示列,因为它们消耗空间且通常比数值型慢。尤其MyISAM引擎中,因为对字符串进行压缩,会导致查询慢很多。

特殊类型数据:某些类型数据并不直接与内置类型一致,低于秒精度的时间戳就是一个例子,另一个是IPv4地址,人们通常使用VARCHAR(15)来储存,然而,它们实际上是32位无符号整数,小数点是为了阅读方便。所以应使用无符号整数
    存储IP地址,MYSQL提供了INET_ATON()和INET_NTOA()函数在这两种表示方法间转换

    

二、Mysql schema设计中的陷阱

太多的列:MYSQL的存储引擎API工作时,需要在服务器层和存储引擎层直接进行通过行缓冲进行拷贝数据,然后再服务器层将缓存内容解析成各个列,从行缓存中将编码过的列转换成行数据结构的操作代价是非常高的。如果使用非常宽的表
    (数千个字段),然而只有一小部分列实际用到,这时转换的代价就会非常高。

太多的关联:MYSQL限制了每个管理操作最多操作61个表,但实际上,不建议关联查询超过12个表以上。

全能的枚举:防止过度使用枚举,如CREATE TABLE ...() country enun('','0','1'...,'31')。这种模式的schema设计非常凌乱,如果枚举需要增加一个新的国家时,就要做一个ALTER TABLE操作。

变相的枚举:枚举允许列中存储一组定义值中的单个值,集合(SET)则允许列中存储一组定义值中的一个或者多个值,这有时候会导致混乱,比如CREATE TABLE ... (sex set('1','0') not null default '1')。如果1和0不会同时出现,那么
    毫无疑问应该使用枚举替代集合。

非此发明的NULL:之前我们讲了应避免使用null的好处,并且尽可能考虑替代方案,即使需要存储一个事实上的空值时,可以考虑0、空字符串代替。但是遵循这个原则也不能走极端,当确实需要表示未知值时,不要害怕使用NULL。

三、范式和反范式
数据库设计三范式:1.第一范式(确保每列保持原子性) 2.第二范式(确保表中的每列都和主键相关) 3.第三范式(确保每列都和主键列直接相关,而不是间接相关)

范式的优点和缺点:当为了性能而寻求帮助时,通常建议对schema进行范式化设计,尤其是写密集的场景,
    范式化通常能带来的好处:
        1.范式化的更新通常比非范式化的快。
        2.当数据范式化较好时,就只有很少或者没有重复数据,update只需要更改最少的代码
        3.范式化的表通常更小,所以操作更快
        4.很少的冗余意味着检索时效率更高
    范式化的缺点:缺点时通常需要关联查询,稍微复杂一点的查询语句在符合范式的schema上可能至少需要一次关联,这不但代价昂贵,还可能使索引无效。

反范式的优点和缺点:反范式的schema因为数据都在一张表上,可以很好避免关联。如果不需要关联,则对大部分查询最差的情况(即没有使用索引,全表扫描),当数据比内存大时这可能比关联要快得多,因为这样避免了随机I/O
实际上,完全的范式化或者反范式化都是实验室才有的东西,真实世界里是混合使用。

四、缓存表和汇总表
有时候提升性能最好的方法是同一张表保存衍生的冗余数据,然而,有时候需要创建一张完全独立的汇总表或者缓存表(特别是为了满足检索时的需求)。如果能容许少量脏数据,这是非常好的方法。

计数器表:如果在表中保存计数器,则更新数据有可能碰到并发问题。计数器表在web应用中很常见,用这种表记录登陆次数、文件下载次数等,创建一个独立的表存储计数器通常是个好主意。问题在于,对于任何想要更新这一行的事务
    来说,这条记录上都有一个全局的互斥锁(nutex),这会使得事务只能串行,要获得更好的并发性能,可以考虑先初始化100条数据,每次随机在100条数据中选择更新,获取时需要sum一下。

加快ALTER TABLE操作速度:MYSQL修改表结构操作方法是用新的结构创建一张表,从旧表中插入新表数据,然后删除旧表。如果内存不足且表很大,ALTER操作有可能花费数个小时甚至数天。

总结:良好的schema设计原则是普遍适用的,但MYSQL有它自己实现细节要注意,概括来说,尽可能保持任何东西的小而简单总是好的。
    1.避免过度设计,例如会导致极其复杂查询的schema设计,或者有很多列的表设计
    2.使用小而简单的合适数据类型,除非真实数据模型中有确切的需要,否则尽可能避免NULL值。
    3.尽量使用相同数据类型存储相似或相关的值,尤其在关联条件中使用的列
    4.注意可变长字符串,尤其在临时表和排序时会按照最悲观的最大长度分配内存
    5.尽量使用整型定义标识列
    6.避免使用MYSQL废弃的特性,例如指定浮点数的精度,或者整数显示宽度
    7.小心使用ENUM和SET,它们使用很方便,但有时候会有陷阱。最好避免使用BIT

猜你喜欢

转载自blog.csdn.net/ws346348183/article/details/89031696