ORM规约变更经典案例---mysql军规
先介绍一下《MySQL数据库开发的三十六条军规》,出自58赶集。
写在前面的话:
总是在灾难发生后,才想起容灾的重要性;
总是在吃过亏后,才记得曾经有人提醒过。
(一)核心军规
(1)不在数据库做运算
cpu计算务必移至业务层;
(2)控制单表数据量
int型不超过1000w,含char则不超过500w;
合理分表;
限制单库表数量在300以内;
(3)控制列数量
字段少而精,字段数建议在20以内;
(4)平衡范式与冗余
效率优先;
往往牺牲范式;
(5)拒绝3B
拒绝大sql语句:big sql
拒绝大事物:big transaction
拒绝大批量:big batch
(二)字段类军规
(6)用好数值类型
tinyint(1Byte)
smallint(2Byte)
mediumint(3Byte)
int(4Byte)
bigint(8Byte)
bad case:int(1)/int(11)
(7)字符转化为数字
用int而不是char(15)存储ip
(8)优先使用enum或set
例如:`sex` enum (‘F’, ‘M’)
(9)避免使用NULL字段
NULL字段很难查询优化;
NULL字段的索引需要额外空间;
NULL字段的复合索引无效;
bad case:
`name` char(32) default null
`age` int not null
good case:
`age` int not null default 0
(10)少用text/blob
varchar的性能会比text高很多;
实在避免不了blob,请拆表;
(11)不在数据库里存图片
这个我不能理解!
但这是赶集网的经验,求detail!
(三)索引类军规
(12)谨慎合理使用索引
改善查询、减慢更新;
索引一定不是越多越好(能不加就不加,要加的一定得加);
覆盖记录条数过多不适合建索引,例如“性别”;
(13)字符字段必须建前缀索引
(14)不在索引做列运算
!!!不只是索引,都不能做列运算吧!!!
bad case:
select id where age +1 = 10;
(15)innodb主键推荐使用自增列;
主键建立聚簇索引;
主键不应该被修改;
字符串不应该做主键;
如果不指定主键,innodb会使用唯一且非空值索引代替;
(16)不用外键
请由程序保证约束;
(四)sql类军规
(17)sql语句尽可能简单
一条sql只能在一个cpu运算;
大语句拆小语句,减少锁时间;
一条大sql可以堵死整个库;
(18)简单的事务
事务时间尽可能短;
bad case:
上传图片事务
(19)避免使用trig/func
触发器、函数不用;
客户端程序取而代之;
(20)不用select *
消耗cpu,io,内存,带宽;
这种程序不具有扩展性;
(21)OR改写为IN()
or的效率是n级别;
in的消息时log(n)级别;
in的个数建议控制在200以内;
select id from t where phone=’159′ or phone=’136′;
=>
select id from t where phone in (’159′, ’136′);
(22)OR改写为UNION
mysql的索引合并很弱智
select id from t where phone = ’159′ or name = ‘john’;
=>
select id from t where phone=’159′
union
select id from t where name=’jonh’
(23)避免负向%
(24)慎用count(*)
(25)同上
(26)limit高效分页
limit越大,效率越低
select id from t limit 10000, 10;
=>
select id from t where id > 10000 limit 10;
(27)使用union all替代union
union有去重开销
(28)少用连接join
(29)使用group by
分组;
自动排序;
(30)请使用同类型比较
(31)使用load data导数据
load data比insert快约20倍;
(32)打散批量更新
(33)新能分析工具
show profile;
mysqlsla;
mysqldumpslow;
explain;
show slow log;
show processlist;
show query_response_time(percona);
案例1:
以下是我们组发生的一个真实例子,具体操作和调研是由我两个同事实施的。
情境:
核心交易目前在进行代码重构,数据模型的重构是其中重要的一个环节。一天我们同事在进行DDL(Data Defination Lauguage)的变更,由于两个字段比较相近,但是其中一个是原有字段不可为空,另外一个是新增字段,允许为空,结果空字段被赋值给了非空字段,DDL执行导致大量异常。DDL变更回滚后日志恢复正常。
任务:
从java程序到连接mysql数据库用到了atlas、mybatis、数据库驱动到达mysql数据。而字段的映射是mybatis这样的ORM(Object Ralational Mapping)框架来处理的,我们的任务就是分析mybatis的源码和配置,找到问题的根源和以后要注意的事项。
行动:
下载mybatis源码进行调试、分析。当前生产环境中,Mybatis版本是3.2.8.
在使用mybatis时,有时可以不定义resultMap,直接在语句上指定resultType。此时涉及到Mybatis的结果集自动映射。Mybatis的自动映射。Mybatis的自动映射默认开启。分析源码理解mybatis结果自动映射原理:
1. mybatis自动映射预处理流程:
2.自动映射流程(applyAutomaticMappings方法)
就是说applyAutomaticMappings要用到两个配置参数:mapUnderscoreToCamelCase和callSettersOnNulls。
mapUnderscoreToCamelCase:是否开启驼峰命名。开启后会对大小写、下划线均不敏感。
callSettersOnNulls:是否在该字段值为null时将结果同时反射set赋值方法进行赋值。
3. 自动驼峰命名规则测试实验
实体属性字段名是否自动驼峰命名是否可以赋值
deviceIddevice_idtrue赋值给deviceId
deviceIddevice_idfalse没有赋值给deviceId
traceno
traceNo
tracenotrue赋值给traceNo
traceno
traceNo
trace_nofalse都没有进行赋值
traceno
traceNo
trace_notrue赋值给traceNo
结论:
在映射时会先把没有在resultMap中定义字段映射的字段按照名称相同的方式自动映射到返回类型的对应属性上。自动映射会忽略下划线和大小写。
Mybatis settings配置项说明应该仔细研读。
字段定义各个字段之间的区分要尽可能的大,严禁使用只有大小写和下划线不同的两个字段。
我们现在在做分享会和读书会,我的想法是这些学习活动要尽量贴近项目,做有深度的学习。代码是随便找个人培训一下就可以写的,但是写出代码的效率和可维护性等代码质量的要求决定了大公司对初级程序员要求的门槛。而对所有技术研究的深度决定了突发问题的解决能力,对后续的建设提出的指导和建议。
案例2:
《逆流而上》里介绍的一个案例。
情境:
在某次项目发布阶段(数据库使用了分库分表),因为业务需要新增表字段,从SQL的代码逻辑来看,使用了select *,新增字段应该是兼容的,但在做线上数据库DDL操作后,立即出现了日志错误数飙升报警。当回滚还未执行,日志错误就已经自动恢复。
任务:
从问题的现象来看,这个问题只有在变更过程中才出现,不太像是结果集映射问题,如果是映射问题,不执行回滚时无法自动恢复的。DBA反馈,可能是TDDL(Taobao Dustributed Data Layer分布式数据访问引擎)层对Select * 的解析逻辑引起DDL变更的不兼容。我们的任务就是确认问题发生的真正原因和对以后的指导意义。
行动(分析过程):
1. TDDL在执行的时候,碰到select *,会从数据库表中解决出对应的全部字段:取第一个库的第一个表进行解析,解析之后,会缓存结果。替换*,然后在吧解析后的SQL语句交到目标数据库执行。
2. 在第一个库变更后,TDDL拿到最新的字段列表,后续一段时间内的查询,都直接用带有新增字段的SQL语句提交到数据库执行;由于有部分数据库还没执行变更,没有新的字段,导致数据库执行出错,无法查询数据。
结论:
对于此问题是分库分表中,持久层框架无对select *的兼容逻辑导致。
但是使用select *的弊端不限于此,比如select * 查询非必需字段,会造成资源浪费甚至影响服务器性能;增加SQL的解析成本;表结构变更可能会引起字段映射问题;不会使用覆盖索引,不利于查询的性能优化等。
《阿里巴巴编程规约》中对于ORM规范,有明确一条强制规约:在表查询中,一律不要使用*作为查询的字段列表,需要哪些字段必须明确写明。
很多人问过我学习方法的问题,我觉得把这些基本规约和军规仔细研读,在平时的工作中多总结实践,也可以算作一个初级或者中级程序员的亮点了。技术追求体现在解决不了的问题追究到底,了解不了的问题研究到底。项目中问题不是天天有,但是这些理论怎样和实际结合确是天天要面对的问题。
跑题时间:
周末,男神在看电视,我照例在旁边垫子上一边做瑜伽一边陪看电视。看的是一部很老的电影《叶问前传》。看完之后我就跟男神分享心得:“你看叶问看起来正直厚道的一个人,做起事情来很讲究方法。想搞定女友,人家先搞定女友他爹。最后女友舍身救叶问,也得到了他爹实际上的支持。”
女孩子和男孩子真的是来自两个星球。我看一个片子通常会想很多。但是我说出来的必定和实际发生的事情有一定的联系。但是男孩子通常是看不出来的。比如我跟男神说的上面的心得。是因为前一天晚上我俩聊天,他说看到一个姑娘比我还要超凡脱俗。
我说你真要和她在一起,知道了她每月要买多少化妆品你就不这么想了。女孩子多半人前人后不一样。
男神说如果在外面见到我,可能没有那么喜欢我。我在外面看起来滑头滑脑的。
我心想自己在外人看来基本上算是老年痴呆,但是职业习惯,见什么人说什么话。不然男神不爱吱声,我又不说话,我俩在别人看来简直一对怪胎。我也不点破,只对男神说:“你用词不当,这个词应该是‘古灵精怪’”。
后来他说想找个小尼姑当小老婆。看样子他还是不太认同,觉得还是应该自己是一根木头,外面看起来就应该是一根木头,也不顾及别人跟你这根木头打交道到底会不会尴尬。
所以我会有机会就旁敲侧击一下:做人要遵从本心,做事要灵活。
关于作者
晓静,20岁时毕业于东北大学计算机系。在毕业后的第一家公司由于出众的语言天赋,在1年的时间里从零开始学日语并以超高分通过了国际日语一级考试,担当两年日语翻译的工作。后就职于人人网,转型做互联网开发。中国科学院心理学研究生。有近百个技术发明专利,创业公司合伙人。有日本东京,美国硅谷技术支持经验。目前任美团点评技术专家(欢迎关注静儿的个人技术公众号:编程一生),心法文章可参考我的《自动化管理之新人培养》
技术交流可关注我的github:https://github.com/xiexiaojing