日常编程中的小技巧以及注意点(四)

之前写过一篇《日常编程中的小技巧以及注意点(三)》,感兴趣可以查看。


1【MySQL】字段允许适当冗余,以提高查询性能,但必须考虑数据一致。

冗余字段应遵循:

  1. 不是频繁修改的字段。
  2. 不是varchar超长字段,更不能是text字段。

正例:商品类目名称使用频率高,字段长度短,名称基本一成不变,可在相关联的表中冗余存储类目名称,避免关联查询。


2【MySQL】当单表行数超过500万行或者单表容量超过2GB时,才推荐进行分库分表

说明:如果预计三年后的数据量无法达到这个级别,请不要在创建表时就分库分表。


3【MySQL】设置合适的字符存储长度,不但可以节约数据库表空间和索引存储,更重要的是能够提升检索速度

正例:见下表,其中无符号值可以避免误存负数,且扩大了数据的表示范围。

对象 年龄区间 类型 字节 表示范围
150岁之内 unsigned tinyint 1 无符号值;0至255
数百岁 unsigned smallint 2 无符号值;0至65535
恐龙化石 数千万年 unsigned int 4 无符号值;0至约42.9亿
太阳 约50亿年 unsigned bigint 8 无符号值;0至约

4【MySQL】业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引

说明:不要以为唯一索引影响了insert速度,这个速度损耗可以忽略,但会明显提高查找速度;另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。


5【MySQL】超过三个表禁止join。需要join的字段,数据类型必须绝对一致;当多表关联查询时,保证被关联的字段需要有索引

说明:即使双表join也要注意表索引、SQL性能。


6【MySQL】建组合索引的时候,区分度最高的在最左边

正例:如果where a=? and b=?,a列几乎接近于唯一值,那么只需要单建idx_a索引即可。

说明:如存在非等号和等号混合判断条件,在建索引时,请把等号条件的列前置。如where a>? and b=?,那么即使a的区分度更高,也必须把b放在索引的最前列。


7【MySQL】count(distinct col)计算该列除NULL外的不重复行数。注意,count(distinct col1, col2),如果其中一列全为NULL,那么即使另一列有不同的值,也返回为0


8【MySQL】当某一列的值全为NULL时,count(col)的返回结果为0,但sum(col)的返回结果为NULL,因此使用sum()时需注意NPE问题

正例:可以使用如下方式来避免sum的NPE问题:SELECT IF(ISNULL(SUM(g)), 0, SUM(g)) FROM table;


9【MySQL】使用ISNULL()来判断是否为NULL值

说明:NULL与任何值的直接比较都为NULL。

  1. NULL<>NULL的返回结果是NULL,而不是false。
  2. NULL=NULL的返回结果是NULL,而不是true。
  3. NULL<>1的返回结果是NULL,而不是true。

10【MySQL】不得使用外键与级联,一切外键概念必须在应用层解决

说明:以学生和成绩的关系为例,学生表中的student_id是主键,那么成绩表中的student_id则为外键。如果更新学生表中的student_id,同时触发成绩表中的student_id更新,即为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。


11【MySQL】数据订正(特别是删除、修改记录操作)时,要先select,避免出现误删除,确认无误才能执行更新语句


12【MySQL】in操作能避免则避免,若实在避免不了,需要仔细评估in后面的集合元素数量,控制在1000个之内


13【MySQL】在表查询中,一律不要使用*作为查询的字段列表,需要哪些字段必须明确写明

说明:1.增加查询分析器解析成本。

           2.增减字段容易与resultMap配置不一致。

SELECT *增加很多不必要的消耗(CPU、IO、内存、网络带宽);增加了使用覆盖索引的可能性;当表结构发生改变时,前断也需要更新。所有要求直接在select后面接上字段名。


14【MySQL】sql.xml配置参数使用:#{},#param#,不要使用${},此种方式容易出现SQL注入


15【MySQL】不要写一个大而全的数据更新接口

传入为POJO类,如果不管是否为自己的目标更新字段进行update table set c1=value1, c2=value2, c3=value3;是不对的。执行SQL时,不要更新无改动的字段,一是容易出错,二是效率低,三是会增加binlog存储。


16 类在设计与实现时要符合单一原则

说明:单一原则是最易理解却又最难实现的一条规则,随着系统的演进,工程师很多时候会忘记类设计的初衷。


17 在系统设计阶段,共性业务或公共行为抽取出来公共模块、公共配置、公共类、公共方法等,在系统中不应出现重复代码的情况

说明:随着代码的重复次数不断增加,维护成本呈指数级上升。


18 尽量早释放无用对象的引用

大部分时,方法局部引用变量所引用的对象会随着方法结束而变成垃圾,因此,大部分时候程序无需将局部引用变量显式设为null。

例如:

public void test() {
    Object obj = new Object();
    //do something...
    obj = null;
}

上面这个就没必要了,随着方法test()的执行完成,程序中obj引用变量的作用域就结束了。但如果是下面这样的情况:

public void test() {
    Object obj = new Object();
    //do something...
    obj = null;
    //执行耗时,耗内存的操作;或调用耗时,耗内存的方法
    //do something...
}

这时候就有必要将obj赋值为null,可以尽早的释放对Object对象的引用。


19 巧判奇数与偶数

判断一个数是否是奇数,你可能会这样做:

if (n % 2 == 1) {
    doSomething();
}

不过我们用与或运算的话会快很多。例如判断是否是奇数,我们就可以把n和1相与了,如果结果为1,则是奇数,否则就不会。即:

if (n & 1 == 1) {
    doSomething();
}

同理,判断偶数就可以用“n & 1 == 0”的方式来进行判断,但是还有一种判断偶数的方式也是比较好的:

private static boolean isPowerOfTwo(int val) {
    return (val & -val) == val;
}

 


20【MySQL】SQL语句中IN包含的值不应过多

MySQL对于IN做了相应的优化,即将IN中的常量全部存储在一个数组里面,而且这个数组是排好序的。但是如果数值较多,产生的消耗也是比较大的。再例如:

select id from t where num in(1,2,3)

对于连续的数值,能用between就不要用in了;再或者使用连接来替换。


21【MySQL】当只需要一条数据的时候,使用limit 1

加上LIMIT 1,只要找到了对应的一条记录,就不会继续向下扫描了,效率会大大提高。LIMIT 1适用于查询结果为1条(也可能为0)会导致全表扫描的SQL语句。

如果要查询的字段是索引的话,就不需要加上LIMIT 1,如果是根据主键查询一条记录也不需要LIMIT 1,主键也是索引。

例如:

SELECT * FROM t_user WHERE id=?;

就不需要写成:

SELECT * FROM t_user WHERE id=? LIMIT 1;

二者效率没有区别。


22【MySQL】如果排序字段没有用到索引,就尽量少排序


23【MySQL】尽量用union all代替union

union和union all的差异主要是前者需要将结果集合并后再进行唯一性过滤操作,这就会涉及到排序,增加大量的CPU运算,加大资源消耗及延迟。当然,union all的前提条件是两个结果集没有重复数据。


24【MySQL】区分in和exists、not in和not exists

select * from 表A where id in (select id from 表B)

上面SQL语句相当于:

select * from 表A where exists(select * from 表B where 表B.id=表A.id)

区分in和exists主要是造成了驱动顺序的改变(这是性能变化的关键),如果是exists,那么以外层表为驱动表,先被访问,如果是IN,那么先执行子查询。所以IN适合于外表大而内表小的情况;EXISTS适合于外表小而内表大的情况。

关于not in和not exists,推荐使用not exists,不仅仅是效率问题,not in可能存在逻辑问题。如何高效地写出一个替代not in的SQL语句呢?

原SQL语句:

select colname ... from A表 where a.id not in (select b.id from B表)

高效的SQL语句:

select colname ... from A表 where not exists(select b.id from B表 where 表B.id=表A.id)

25【MySQL】使用合理的分页方式以提高分页的效率

select id,name from product limit 866613,20

使用上述SQL语句做分页的时候,可能有人会发现,随着表数据量的增加,直接使用limit分页查询会越来越慢。

优化的方法如下:可以取前一页的最大行数的id,然后根据这个最大的id来限制下一页的起点。比如此列中,上一页最大的id是866613。SQL可以采用如下的写法:

select id,name from product where id>866613 limit 20

26【MySQL】避免在where子句中对字段进行表达式操作

比如:

select user_id,user_project from user_base where age*2=36;

上述SQL中对字段进行了算术运算,这会造成引擎放弃使用索引,建议改成:

select user_id,user_project from user_base where age=36/2;

27【MySQL】注意范围查询语句

对于联合索引来说,如果存在范围查询,比如between、>、<等条件时,会造成后面的索引字段失效。


28【MySQL】尽量使用inner join,避免使用left join

参与联合查询的表至少为2张表,一般都存在大小之分。如果连接方式是inner join,在没有其他过滤条件的情况下MySQL会自动选择小表作为驱动表,但是left join在驱动表的选择上遵循的是左边驱动右边的原则,即left join左边的表名为驱动表。

被驱动表的索引字段作为on的限制字段。

你可能感兴趣的:(编程技巧)