老叶观点:MySQL开发规范之我见

大多数MySQL规范在网上也都能找得到相关的分享,在这里要分享的是老叶个人认为比较重要的,或者容易被忽视的,以及容易被混淆的一些地方。

1、默认使用InnoDB引擎
【老叶观点】已多次呼吁过了,InnoDB适用于几乎99%的MySQL应用场景,而且在MySQL 5.7的系统表都改成InnoDB了,还有什么理由再死守MyISAM呢。

此外,频繁读写的InnoDB表,一定要使用具有自增/顺序特征的整型作为显式主键。

参考】:[MySQL FAQ]系列 — 为什么InnoDB表要建议用自增列做主键

 

2、字符集选择utf-8
【老叶观点】若为了节省磁盘空间,则建议选择latin1。建议选择utf-8通常是为了所谓的“通用性”,但事实上用户提交的utf-8数据也一样可以以latin1字符集存储。

用latin1存储utf-8数据可能遇到的麻烦是,如果有基于中文的检索时,可能无法100%准确(老叶亲自简单测试常规的中文完检索全不是问题,也就是一般的中文对比是没问题的)。

用latin1字符集存储utf-8数据的做法是:在web端(用户端)的字符集是utf-8,后端程序也采用utf-8来处理,但 character_set_client、character_set_connection、character_set_results、character_set_database、character_set_server 这几个都是 latin1,且数据表、字段的字符集也是latin1。或者说数据表采用latin1,每次连接后执行 SET NAMES LATIN1 即可。

参考】:小谈MySQL字符集

 

3、InnoDB表行记录物理长度不超过8KB
【老叶观点】InnoDB的data page默认是16KB,基于B+Tree的特点,一个data page中需要至少存储2条记录。因此,当实际存储长度超过8KB(尤其是TEXT/BLOB列)的大列(large column)时会引起“page-overflow存储”,类似ORACLE中的“行迁移”。

因此,如果必须使用大列(尤其是TEXT/BLOB类型)且读写频繁的话,则最好把这些列拆分到子表中,不要和主表放在一起存储。如果不太频繁,可以考虑继续保留在主表中。

当然了,如果将 innodb_page_size 选项修改成 8KB,那么行记录物理长度建议不超过4KB。

参考】:[MySQL优化案例]系列 — 优化InnoDB表BLOB列的存储效率

 

4、是否使用分区表
【老叶观点】在一些使用分区表后明显可以提升性能或者运维便利性的场景下,还是建议使用分区表。

比如老叶就在zabbix的数据库采用TokuDB引擎的前提下,又根据时间维度使用了分区表。这样的好处是保证zabbix日常应用不受到影响前提下,方便管理员例行删除过去数据,只需要删除相应分区即可,不需再执行一个非常慢的DELETE而影响整体性能。

参考】:迁移Zabbix数据库到TokuDB

 

5、是否使用存储过程、触发器
【老叶观点】在一些合适的场景下,用存储过程、触发器也完全没问题。

我们以前就是利用存储完成游戏业务逻辑处理,性能上不是问题,而且一旦需求有变更,只需修改存储过程,变更代价很低。我们还利用触发器维护一个频繁更新的表,对这个表的所有变更都将部分字段同步更新到另一个表中(类似物化视图的变相实现),也不存在性能问题。

不要把MySQL的存储过程和触发器视为洪水猛兽,用好的话,没有问题的,真遇到问题了再优化也不迟。另外,MySQL因为没有物化视图,因此视图能不用就尽量少用吧。

 

6、选择合适的类型
【老叶观点】除了常见的建议外,还有其他几个要点:

6.1、用INT UNSIGNED存储IPV4地址,用INET_ATON()、INET_NTOA()进行转换,基本上没必要使用CHAR(15)来存储。

6.2、枚举类型可以使用ENUM,ENUM的内部存储机制是采用TINYINT或SMALLINT(并非CHAR/VARCHAR),性能一点都不差,记住千万别用CHAR/VARCHAR 来存储枚举数据。

6.3、还个早前一直在传播的“常识性误导”,建议用TIMESTAMP取代DATETIME。其实从5.6开始,建议优先选择DATETIME存储日期时间,因为它的可用范围比TIMESTAMP更大,物理存储上仅比TIMESTAMP多1个字节,整体性能上的损失并不大。

6.4、所有字段定义中,默认都加上NOT NULL约束,除非必须为NULL(但我也想不出来什么场景下必须要在数据库中存储NULL值,可以用0来表示)。在对该字段进行COUNT()统计时,统计结果更准确(值为NULL的不会被COUNT统计进去),或者执行 WHERE column IS NULL 检索时,也可以快速返回结果。

6.5、尽可能不要直接 SELECT * 读取全部字段,尤其是表中存在 TEXT/BLOB 大列的时候。可能本来不需要读取这些列,但因为偷懒写成 SELECT * 导致内存buffer pool被这些“垃圾”数据把真正需要缓冲起来的热点数据给洗出去了。

 

7、关于索引
【老叶观点】除了常见的建议外,还有几个要点:

7.1、超过20个长度的字符串列,最好创建前缀索引而非整列索引(例如:ALTER TABLE t1 ADD INDEX(user(20))),可以有效提高索引利用率,不过它的缺点是对这个列排序时用不到前缀索引。前缀索引的长度可以基于对该字段的统计得出,一般略大于平均长度一点就可以了。

7.2、定期用 pt-duplicate-key-checker 工具检查并删除重复的索引。比如 index idx1(a, b) 索引已经涵盖了 index idx2(a),就可以删除 idx2 索引了。

7.3、有多字段联合索引时,WHERE中过滤条件的字段顺序无需和索引一致,但如果有排序、分组则就必须一致了。

比如有联合索引 idx1(a, b, c),那么下面的SQL都可以完整用到索引

SELECT ... WHERE b = ? AND c = ? AND a = ?;  --注意到,WHERE中字段顺序并没有和索引字段顺序一致
SELECT ... WHERE b = ? AND a = ? AND c = ?;
SELECT ... WHERE a = ? AND b IN (?, ?) AND c = ?;
SELECT ... WHERE a = ? AND b = ? ORDER BY c;
SELECT ... WHERE a = ? AND b IN (?, ?) ORDER BY c;
SELECT ... WHERE a = ? ORDER BY b, c;
SELECT ... ORDER BY a, b, c;  -- 可利用联合索引完成排序

 

而下面几个SQL则只能用到部分索引,或者可利用到ICP特性

SELECT ... WHERE b = ? AND a = ?; -- 只能用到 (a, b) 部分
SELECT ... WHERE a IN (?, ?) AND b = ?; -- EXPLAIN显示只用到 (a, b) 部分索引,同时有ICP
SELECT ... WHERE (a BETWEEN ? AND ?) AND b = ?; -- EXPLAIN显示只用到 (a, b) 部分索引,同时有ICP
SELECT ... WHERE a = ? AND b IN (?, ?); -- EXPLAIN显示只用到 (a, b) 部分索引,同时有ICP
SELECT ... WHERE a = ? AND (b BETWEEN ? AND ?) AND c = ?; -- EXPLAIN显示用到 (a, b, c) 整个索引,同时有ICP
SELECT ... WHERE a = ? AND c = ?; -- EXPLAIN显示只用到 (a) 部分索引,同时有ICP
SELECT ... WHERE a = ? AND c >= ?; -- EXPLAIN显示只用到 (a) 部分索引,同时有ICP

ICP(index condition pushdown)是MySQL 5.6的新特性,其机制会让索引的其他部分也参与过滤,减少引擎层和server层之间的数据传输和回表请求,通常情况下可大幅提升查询效率。

 

下面的几个SQL完全用不到该索引

SELECT ... WHERE b = ?;
SELECT ... WHERE b = ? AND c = ?;
SELECT ... WHERE b = ? AND c = ?;
SELECT ... ORDER BY b;
SELECT ... ORDER BY b, a;

 

从上面的几个例子就能看的出来,以往强调的WHERE条件字段顺序要和索引顺序一致才能使用索引的 “常识性误导” 无需严格遵守。

此外,有些时候查询优化器指定的索引或执行计划可能并不是最优的,可以手工指定最优索引,或者修改session级的 optimizer_switch 选项,关闭某些导致效果反而更差的特性(比如index merge通常是好事,但也遇到过用上index merge后反而更差的,这时候要么强制指定其中一个索引,要么可以临时关闭 index merge 特性)。

 

8、其他8
.1、哪怕是基于索引的条件过滤,如果优化器意识到总共需要扫描的数据量超过30%时(ORACLE里貌似是20%,MySQL目前是30%,没准以后会调整),就会直接改变执行计划为全表扫描,不再使用索引。

8.2、多表JOIN时,要把过滤性最大(不一定是数据量最小哦,而是只加了WHERE条件后过滤性最大的那个)的表选为驱动表。此外,如果JOIN之后有排序,排序字段一定要属于驱动表,才能利用驱动表上的索引完成排序。

8.3、绝大多数情况下,排序的代价通常要来的更高,因此如果看到执行计划中有 Using filesort,优先创建排序索引吧。

8.4、利用 pt-query-digest 定期分析slow query log,并结合 Box Anemometer 构建slow query log分析及优化系统。

参考】:[MySQL FAQ]系列 — EXPLAIN结果中哪些信息要引起关注

 

备注:若无特别说明,以上规范建议适用于MySQL 5.6及之前的版本(并且主要是5.6版本,尤其是ICP特性、DATETIME变化这两个地方)。5.7及之后的版本可能会有些变化,个别规范建议需要相应调整。

 

延伸阅读:

 

关于MySQL的方方面面大家想了解什么,可以直接留言回复,我会从中选择一些热门话题进行分享。 同时希望大家多多转发,多一些阅读量是老叶继续努力分享的绝佳助力,谢谢大家 :)

文章转自老叶茶馆公众号,原文链接:http://imysql.com/2015/07/23/something-important-about-mysql-design-reference.shtml

时间: 2024-10-03 15:09:17

老叶观点:MySQL开发规范之我见的相关文章

从MySQL开发规范处看创业

导读 MySQL是时下热度仅次于Oracle的关系型数据库,因为便捷高效的特点风靡整个DB行业.而创业呢,政策层面的"双创"让多少热血青年跳进了创业这个"火坑",从去年的内容创业到共享经济,从罗辑思维.吴晓波频道到摩拜单车.ofo.只要提及创业,必讲互联网,必讲商业模式.大数据.DAU.变现.人工智能.认知.消费升级.中产崛起.但是这些概念和认知与MySQL的开发规范又有什么关系呢,还真真有关系,并且能联系起来,你不信啊,你自己往下看咯. 首先第一条,表的存储引擎必

MySQL开发规范

  1.库名.表名.字段名必须使用小写字母,并采用下划线分割. a)MySQL有配置参数lower_case_table_names,不可动态更改,linux系统默认为 0,即库表名以实际情况存储,大小写敏感.如果是1,以小写存储,大小写不敏感.如果是2,以实际情况存储,但以小写比较. b)如果大小写混合使用,可能存在abc,Abc,ABC等多个表共存,容易导致混乱. c)字段名显式区分大小写,但实际使用不区分,即不可以建立两个名字一样但大小写不一样的字段. d)为了统一规范, 库名.表名.字段

DBA 谈互联网 MySQL 开发规范

写在前面:无规矩不成方圆.对于刚加入互联网的朋友们,肯定会接触到MySQL,MySQL作为互联网最流行的关系型数据库产品,它有它擅长的地方,也有它不足的短板,针对它的特性,结合互联网大多应用的特点,笔者根据自己多年互联网公司的MySQL DBA经验,现总结出互联网MySQL的一些开发规范,仅供参考. 基础规范  (1) 使用INNODB存储引擎 (2) 表字符集使用UTF8 (3) 所有表都需要添加注释 (4) 单表数据量建议控制在5000W以内 (5) 不在数据库中存储图⽚.文件等大数据 (6

mysql开发规范文档

mysql开发规范文档 1.mysql数据库命名规范 2.表结构设计.数据类型选择 3.开发注意事项 www.bitsCN.com 1.mysql数据库命名规范 1.1 命名规范: Table/view/procedure/function/package: object类型简称+功能模块简称的小写字母+"_"+业务意义小写单词,如: 主键/外键/索引: object类型简称+"_"+表名简称+字段名 object类型简称: Table:t View:v Proce

老叶倡议:MySQL压力测试基准值

通常,我们会出于以下几个目的对MySQL进行压力测试: 1.确认新的MySQL版本性能相比之前差异多大,比如从5.6变成5.7,或者从官方版本改成Percona分支版本: 2.确认新的服务器性能是否更高,能高多少,比如CPU升级了.阵列卡cache加大了.从机械盘换成SSD盘了: 3.确认一些新的参数调整后,对性能影响多少,比如 innodb_flush_log_at_trx_commit.sync_binlog 等参数: 4.确认即将上线的新业务对MySQL负载影响多少,是否能承载得住,是否需

【MySQL】数据库开发规范

根据工作经验 总结了一份MySQL 数据库开发规范,对于索引设计概况不完全,这点需要DBA针对开发做数据库优化设计的分享,欢迎各取所需.

面向对象的css:团队协作开发规范和按结构划分模块

文章简介:面向对象的css有两个主要原则:separate the structure from the skin,separate the container from the content.第一个原则体现在模块化思想可以理解为,模块的设计制作和布局框架本身相分离,意味着你的模块不能只为某个布局而编写样式,像微博这类存在换肤功能的产 说起模块化,也许我们首先想到的是编程中的模块设计,以功能块为单位进行程序设计,最后通过模块的选择和组合构成最终产品.把这种思想运用到页面构建中,也已经不是什么新

China.com网站开发规范(1)

规范|开发规范|开发规范 1数据库使用规范1.1服务器上有关数据库的一切操作只能由服务器管理人员进行.1.2程序中访问数据库时使用统一的用户.统一的连接文件访问数据库.1.3原则上每一个频道只能建一个库,库名与各频道的英文名称相一致,库中再包含若干表.比较大的.重点的栏目可以考虑单独建库,库名与栏目的英文名称相一致.1.4命名:(1) 数据库.表.字段.索引.视图等一系列与数据库相关的名称必须全部使用与内容相关的英文单词命名(尽量避免使用汉语拼音),对于一个单词难以表达的,可以考虑用多个单词加下

简明HTML CSS开发规范

css|规范|开发规范 //总论 本规范既是一个开发规范,也是一个脚本语言参考,本规范并不是一个一成不变的必须严格遵守的条文,特殊情况下要灵活运用,做一定的变通.但是,请大家千万不要随意更改规范.如果有任何问题,请及时与我联系,我会及时更改本规范的相关代码样例和文档. /基 本 要 求 1. 在网站根目录中开设images common temp 三个子目录,根据需要再开设media 子目录,images目录中放不同栏目的页面都要用到的公共图片,例如公司的标志.banner 条.菜单.按钮等等: