本文主要通过数据库表设计、sql处理、应用层优化三个层面,来谈谈笔者在工作中的一些心得和建议。
合理设计数据库表
合理添加索引
(1)经常进行条件查询(where或join on)的字段考虑加上索引
(2)经常分组(GROUP BY)或排序(ORDER BY)的字段考虑加上索引(Btree)
(3)差异性大的字段才适合建立索引。
差异性表示某行数据的字段在所有数据中是否足够独特,鹤立鸡群。就好像qq号,独一无二,全部数据中的qq号都不一样;相对而言,性别男或者女,都有近一半是相同的,这时候加上索引效果并不理想。
有一种情况,例如某个文章的状态status(0未审核,1审核通过待发布,2已发布,3审核不通过),我们应用层有个定时器(10秒一次)在扫数据库中 审核通过待发布的,然后进行发布,更新status为2。在实际数据中,尽管大部分数据都是已发布(差异性小),但审核通过待发布的数据是非常少的(差异性较大),这时候也可以考虑加上索引(用于where status=1的条件查询则非常有效)。
(4)当需要保证数据某个重要字段的唯一性时,考虑加上唯一索引(做数据库级别的限制往往能避免很多因为错误数据导致的问题)
注:索引并不是越多越好,因为增加索引后,新增(insert)或更新对应字段(update)时,索引会做对应的调整,效率会有一定影响,索引越多影响越大。
表字段个数不要太多
我们公司是建议业务相关字段10个以内。
一般最初设计阶段,某个业务表有很多字段时,可以考虑拆分。一是按业务逻辑分、二是根据预估使用频率分。
避免使用触发器、函数、存储过程等
函数、触发器这些往往在应用层就能处理,而开发人员可以直接管理,并不需要和DBA打过多的交道,移植也方便。
另外数据库是拿来存数据的,除了增删改查,其他都不建议让数据库层面来干了。
注意 varchar 长度,取合理长度即可
varchar 超过255会多1个字节存储该字段值的长度;比如varchar(255)使用1个字节存储值长度,而varchar(256)则需要2个。
而定义的长度会影响对应的索引长度,比如 varchar(100) 和 varchar(20) 其对应建立的索引,前者要比后者的长度要大。
所以在满足业务场景的前提下,尽量取小的值即可。
小数(点)的设计,考虑使用decimal
在业务中往往包含一些小数的设计,例如价格(9.99元)、重量(51.3kg)等。
因为float和double这种浮点类型都存在精度问题,当需要较高精度时,可以使用decimal来取代;
另外一种达到要求精度的方案是降低字段表示的单位,从而消除小数,例如9.99元=999分,51.3kg=51300g,对应的字段使用整数表示即可。这种设计可能会增加应用层的复杂度(需要单位转换等),但如果经常进行排序的话,会有一定优势。
而在一些金融相关的行业,有时候往往不仅仅精确小数点后面两三位,而是4位、5位甚至更多位(例如0.9065元),这时候最好使用decimal。
注:decimal(10,4) 中,4表示小数位数,插入时超出则进行截取;10表示含小数在内的最多位数,这里整数则最多 (10-4) 位,超出时则报错。
sql篇
尽量避免联表查询
- 联表查询时,数量尽量不要超过2个
- 使用单表某些数据缓存(redis、memcached等)+ 应用层代码实现联表查询。
- 必要时,联表查询的条件列加上索引
大量数据时需要分页时 用索引字段条件查询 + limit N 替代直接 limit M,N
在大量数据时,mysql的limit M,N分页是存在比较严重的性能问题的,因为其会扫足前面M+N条数据,然后再取N条;当M非常大时,如2000万+时,效率就会非常慢了(10s+);
其中一个解决方法是:适当结合索引字段(如主键id)+ limit N 就能避免这个问题。
如 :select id, userName from t_user limit 20 000 000, 50;
假设第2000万条数据的id为 22 000 000;
改为:select id, userName from t_user where id >= 22 000 000 limit 50;
尽管在实际中客户端往往没有这样需求(页码太大),但当需要加载所有数据时则经常用到(如缓存预热)。
不要对条件查询的字段进行运算或函数操作
反例:
(1) select id from t_user where age/2 > 10;
(2) select id from t_user where year(birthday) < 2000;
当age或birthday存在索引时,上面的语句并不会使用索引,而是直接进行全表扫描。
修改为:
(1) select id from t_user where age > 20;
(2) select id from t_user where birthday < ‘2000-01-01’;
上面的转换均可以在应用层的代码实现,尽量不要在数据库层面进行运算操作。
经典优化
- name字段有索引,name like ‘%zhang%’ 没有用上索引,而name like ‘zhang%’则用上。
- 联合索引(name, age) 最左匹配,where name=’li’ 或 where name=’li’ and age=18 用上该索引,而where age=18 则无法用到该索引。
- 连接查询一般 优于 子查询
- exist 优于 in
- 范围查询 between and 优于 in
- insert into t values(..),(..); 优于多条 insert into t (…);,因为前者索引只做了一次调整。
应用层优化
能在应用层处理的就不要在数据库处理
数据库连接数量时有限的,数据库处理的东西越少,压力越小,处理时间越短,连接放回连接池的速度就越快,整个应用的吞吐量也会随之增大。
所以一些条件运算、函数计算等都可以尽量在应用层面进行。
注:要综合考虑代价,不要什么都不管就往应用层里面怼。当运用到索引的分组、排序往往在数据库中处理效率更高。
合理利用缓存
一些热点数据可以进行缓存,这时候大流量下,可以大大减少DB的压力。
利用消息机制进行异步DB操作
高并发下,DB往往扛不住,要么直接被压垮,要么连接数远远不够用。
目前来说,利用消息机制进行异步处理(相关DB操作)是一个不错的选择,因为消费的线程数量是有限的(DB可以承受的范围),这时候DB不会有那么大的压力,可以保证整个应用和DB都正常运行。
当结合缓存时,就能保证异步处理期间,仍认为数据已经被正常处理的了,因为取数据时,优先取缓存中的而不是DB。
选择适合的框架
为了简化开发往往会选择对DB操作的框架,而这个的选择在数流量/数据量小时往往没有什么区别。但是一旦到哪里都在抠性能时,这里的优化往往是非常重要的。
一个是考虑公司情况(对哪种框架处理更有经验),另外一个是考虑业务场景,不同的框架往往有其擅长的地方。
如果选择不合适(可能框架本身原因,肯能是因为对框架缺乏了解),想要优化时,往往显得捉襟见肘,难以应对。
总结
- 数据库是拿来存东西的,擅长的是增删改查,而不是各种逻辑处理和计算
- 结合实际业务场景,才能设计出最合理的字段,不长不短,不多不少
- sql优化归根结底都是怎样把索引给用上,把索引的作用最大化
- 善用缓存
原文链接 大专栏 https://www.dazhuanlan.com/2019/08/17/5d5774337b04d/
原文地址:https://www.cnblogs.com/chinatrump/p/11417377.html