最近一段时间一边在线上抓取SQL来优化,一边在整理这个开发规范,尽量减少新的问题SQL进入生产库。今天也是对公司的开发做了一次培训,PPT就不放上来了,里面有十来个生产SQL的案例。因为规范大部分还是具有通用性,所以也借鉴了像去哪儿和赶集的规范,但实际在撰写本文的过程中,每一条规范的背后无不是在工作中有参照的反面例子的。如果时间可以的话,会抽出一部分或分析其原理,或用案例证明。
t_crm_relation
,中间的 crm 代表业务模块名view_
开头,事件以event_
开头,触发器以trig_
开头,存储过程以proc_
开头,函数以func_
开头idx_col1_col2
命名,唯一索引以uk_col1_col2
命名(可去掉f_公共部分)。如 idx_companyid_corpid_contacttime(f_company_id,f_corp_id,f_contact_time)库名、表名、字段名支持最多64个字符,但为了统一规范、易于辨识以及减少传输量,禁止超过32个字符
如 t_crm_relation_tmp0425。备份表也类似,形如 _bak20160425
。
这也是为将来有可能分表做准备的,比如t_crm_ec_record_201403
,但像 t_crm_contact_at201506就打破了这种规范。
不具有时间特性的,直接以 t_tbname_001
这样的方式命名。
5.5版本开始mysql默认存储引擎就是InnoDB,5.7版本开始,系统表都放弃MyISAM了。
SET NAMES UTF8;
。(对于已经在项目中长期使用latin1的,救不了了)即使2个表的字段有明确的外键参考关系,也不使用 FOREIGN KEY ,因为新纪录会去主键表做校验,影响性能。
比如不同表中都有 f_user_id 字段,那么它的类型、字段长度要设计成一样
尽量避免extra列出现:Using File Sort,Using Temporary,rows超过1000的要谨慎上线。
explain解读
type
:ALL, index, range, ref, eq_ref, const, system, NULL(从左到右,性能从差到好)possible_keys
:指出MySQL能使用哪个索引在表中找到记录,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用key
:表示MySQL实际决定使用的键(索引)ref
:表示选择 key
列上的索引,哪些列或常量被用于查找索引列上的值rows
:根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数Extra
Using temporary
:表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询Using filesort
:MySQL中无法利用索引完成的排序操作称为“文件排序”INNODB存储引擎中,secondary index(非主键索引,又称为辅助索引、二级索引)没有直接存储行地址,而是存储主键值。
如果用户需要查询secondary
index中所不包含的数据列,则需要先通过secondary
index查找到主键值,然后再通过主键查询到其他数据列,因此需要查询两次。覆盖索引则可以在一个索引中获取所有需要的数据列,从而避免回表进行二次查找,节省IO因此效率较高。
例如SELECT email,uid FROM user_email WHERE uid=xx,如果uid不是主键,适当时候可以将索引添加为index(uid,email),以获得性能提升。
如不在定义了 ON UPDATE CURRENT_STAMP 的列上创建索引,维护成本太高(好在mysql有insert buffer,会合并索引的插入)
即使需要所有字段,减少网络带宽消耗,能有效利用覆盖索引,表结构变更对程序基本无影响
在保证数据不会有误的前提下,能确定结果集数量时,多使用limit,尽快的返回结果。
转换规则
a. 两个参数至少有一个是 NULL 时,比较的结果也是 NULL,例外是使用 <=> 对两个 NULL 做比较时会返回 1,这两种情况都不需要做类型转换
b. 两个参数都是字符串,会按照字符串来比较,不做类型转换
c. 两个参数都是整数,按照整数来比较,不做类型转换
d. 十六进制的值和非数字做比较时,会被当做二进制串
e. 有一个参数是 TIMESTAMP 或 DATETIME,并且另外一个参数是常量,常量会被转换为 timestamp
f. 有一个参数是 decimal 类型,如果另外一个参数是 decimal 或者整数,会将整数转换为 decimal 后进行比较,如果另外一个参数是浮点数,则会把 decimal 转换为浮点数进行比较
g. 所有其他情况下,两个参数都会被转换为浮点数再进行比较。
如果一个索引建立在string类型上,如果这个字段和一个int类型的值比较,符合第 g 条。如f_phone定义的类型是varchar,但where使用f_phone in (098890),两个参数都会被当成成浮点型。发生这个隐式转换并不是最糟的,最糟的是string转换后的float,mysql无法使用索引,这才导致了性能问题。如果是 f_user_id = ‘1234567’ 的情况,符合第 b 条,直接把数字当字符串比较。
会导致索引失效,有这种搜索需求是,考虑其它方案,如sphinx全文搜索
小于5.6版本时,子查询效率很低,不像Oracle那样先计算子查询后外层查询。5.6版本开始得到优化
超过500个值使用批量的方式,否则一次执行会影响数据库的并发能力,因为单SQL只能且一直占用单CPU,而且可能导致主从复制延迟
比如在一个事务里进行多个select,多个update,如果是高频事务,会严重影响MySQL并发能力,因为事务持有的锁等资源只在事务rollback/commit时才能释放。但同时也要权衡数据写入的一致性。
这种查询更多的是通过索引去优化,但order by的字段有讲究,比如主键id与f_time都是顺序递增,那就可以考虑order by id而非 f_time 。
与上面不同的是,order by之前有个范围查询,由前面的内容可知,用不到类似(c1,c2)的索引,但是可以利用(c2,c1)索引。另外还可以改写成join的方式实现。
建议使用合理的分页方式以提高分页效率,大页情况下不使用跳跃式分页
假如有类似下面分页语句:
SELECT FROM table1 ORDER BY ftime DESC LIMIT 10000,10;
这种分页方式会导致大量的io,因为MySQL使用的是提前读取策略。
推荐分页方式:
SELECT FROM table1 WHERE ftime < last_time ORDER BY ftime DESC LIMIT 10
即传入上一次分页的界值
SELECT * FROM table as t1 inner JOIN (SELECT id FROM table ORDER BY time LIMIT 10000,10) as t2 ON t1.id=t2.id
select最多导致数据库慢,写操作才是锁表的罪魁祸首
INSERT ... ON DUPLICATE KEY UPDATE ...
,插入行后会导致在一个UNIQUE索引或PRIMARY KEY中出现重复值,则执行旧行UPDATE,如果不重复则直接插入,影响1行。REPLACE INTO
类似,但它是冲突时删除旧行。INSERT IGNORE
相反,保留旧行,丢弃要插入的新行。原文:https://www.cnblogs.com/zping/p/10895237.html