首页 > 技术文章 > 数据库优化

lxk233 2018-03-24 17:04 原文

1、选取最合适的字段属性

  • 表越小,查询越快,将字段的宽度设的尽可能小。
  • 尽量把字段设置为not null ,这样执行查询的时候不用比较null值。
  • 省份和性别可以定义成Enum型,因为数值型数据处理起来比文本快的多

2、使用连接(join)来代替子查询

  子查询需要在内存中创建临时表来完成,这个逻辑上需要俩步来完成查询。

3、使用联合(union)来代替手动创建的临时表

  把需要使用临时表的俩条或更多的select查询合并在一个查询中,注:只要用union作为关键字把多个select语句连接起来就可以了,所有select语句中字段数目要相同。

4、事务

  • 可以保持数据的一致性和完整性。事务以begin开始,commit结束。
  • 事务的另一个作用就是:当多个用户同时使用相同的数据源时,它可以利用锁定数据库的方法为用户提供一种安全的访问方式,可以保证用户的操作不被其它的用户所干扰。

5、锁定表

  write  locktable

6、使用外键

  使用外键一定要记住在创建表的时候将表的类型定义为事务安全表InneDB类型。定义的方法是在createtable语句中加上TYPE=InnoDB.

7、使用索引

  索引应建立在那些用于join where ,order by 排序的字段上,不要对数据库中有大量重复的值的字段建立索引。

8、优化查询语句

  • 最好在相同类型的字段间进行比较的操作。
  • 在建有索引的字段上尽量不要使用函数进行操作。
  • 在搜索字符型字段时,使用Like关键字和通配符是以牺牲性能为代价(不建议)
  • 避免在查询中让mysql进行自动类型转换,因为在转换过程中会使索引不起作用。

 sql语句优化

  

1.查询的模糊匹配

尽量避免在一个复杂查询里面使用 LIKE '%parm1%'—— 红色标识位置的百分号会导致相关列的索引无法使用,最好不要用。

解决办法:

其实只需要对该脚本略做改进,查询速度便会提高近百倍。改进方法如下:

a、修改前台程序——把查询条件的供应商名称一栏由原来的文本输入改为下拉列表,用户模糊输入供应商名称时,直接在前台就帮忙定位到具体的供应商,这样在调用后台程序时,这列就可以直接用等于来关联了。

b、直接修改后台——根据输入条件,先查出符合条件的供应商,并把相关记录保存在一个临时表里头,然后再用临时表去做复杂关联。

2.索引问题

在做性能跟踪分析过程中,经常发现有不少后台程序的性能问题是因为缺少合适索引造成的,有些表甚至一个索引都没有。这种情况往往都是因为在设计表时,没去定义索引,而开发初期,由于表记录很少,索引创建与否,可能对性能没啥影响,开发人员因此也未多加重视。然一旦程序发布到生产环境,随着时间的推移,表记录越来越多。这时缺少索引,对性能的影响便会越来越大了。

法则:不要在建立的索引的数据列上进行下列操作:

避免对索引字段进行计算操作

避免在索引字段上使用not,<>,!=

避免在索引列上使用IS NULL和IS NOT NULL

避免在索引列上出现数据类型转换

避免在索引字段上使用函数

避免建立索引的列中使用空值

3.复杂操作

部分UPDATE、SELECT 语句 写得很复杂(经常嵌套多级子查询)——可以考虑适当拆成几步,先生成一些临时数据表,再进行关联操作。

4.update

同一个表的修改在一个过程里出现好几十次,如:

update table1  

set col1=...  

where col2=...;  

update table1  

set col1=...  

where col2=...  

... 

这类脚本其实可以很简单就整合在一个UPDATE语句来完成(前些时候在协助xxx项目做性能问题分析时就发现存在这种情况)

5.在可以使用UNION ALL的语句里,使用了UNION

UNION 因为会将各查询子集的记录做比较,故比起UNION ALL ,通常速度都会慢上许多。一般来说,如果使用UNION ALL能满足要求的话,务必使用UNION ALL。还有一种情况大家可能会忽略掉,就是虽然要求几个子集的并集需要过滤掉重复记录,但由于脚本的特殊性,不可能存在重复记录,这时便应该使用 UNION ALL,如xx模块的某个查询程序就曾经存在这种情况,见,由于语句的特殊性,在这个脚本中几个子集的记录绝对不可能重复,故可以改用UNION ALL)。

6.在WHERE 语句中,尽量避免对索引字段进行计算操作

这个常识相信绝大部分开发人员都应该知道,但仍有不少人这么使用,我想其中一个最主要的原因可能是为了编写写简单而损害了性能,那就不可取了。9月份在对XX系统做性能分析时发现,有大量的后台程序存在类似用法,如:where trunc(create_date)=trunc(:date1),虽然已对create_date 字段建了索引,但由于加了TRUNC,使得索引无法用上。此处正确的写法应该是where create_date>=trunc(:date1) and create_date< pre=""><>或者是where create_date between trunc(:date1) and trunc(:date1)+1-1/(24*60*60)。

注意:因between 的范围是个闭区间(greater than or equal to low value and less than or equal to high value.),故严格意义上应该再减去一个趋于0的小数,这里暂且设置成减去1秒(1/(24*60*60)),如果不要求这么精确的话,可以略掉这步。

7.对Where 语句的法则

7.1 避免在WHERE子句中使用in,not  in,or 或者having。

可以使用 exist 和not exist代替in和not in。

可以使用表链接代替 exist。Having可以用where代替,如果无法代替可以分两步处理。

例子

SELECT * FROM ORDERS WHERE CUSTOMER_NAME NOT IN   

(SELECT CUSTOMER_NAME FROM CUSTOMER)  

优化

SELECT * FROM ORDERS WHERE CUSTOMER_NAME not exist   

(SELECT CUSTOMER_NAME FROM CUSTOMER) 

7.2 不要以字符格式声明数字,要以数字格式声明字符值。(日期同样)否则会使索引无效,产生全表扫描。

例子使用:

SELECT emp.ename, emp.job FROM emp WHERE emp.empno = 7369;

--不要使用:

SELECT emp.ename, emp.job FROM emp WHERE emp.empno = '7369'

8.对Select语句的法则

在应用程序、包和过程中限制使用select * from table这种方式。看下面例子

--使用

SELECT empno,ename,category FROM emp WHERE empno = '7369'

--而不要使用

SELECT * FROM emp WHERE empno = '7369'

9. 排序

避免使用耗费资源的操作,带有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的SQL语句会启动SQL引擎 执行,耗费资源的排序(SORT)功能. DISTINCT需要一次排序操作, 而其他的至少需要执行两次排序。

10.临时表

慎重使用临时表可以极大的提高系统性能。

 

11.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。  

12.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如: 

select id from t where num is null

可以在num上设置默认值0,确保表中num列没有null值,然后这样查询: 

select id from t where num=0  

 

13.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。

14.应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:

select id from t where num=10 or num=20   

可以这样查询:

select id from t where num=10 

union all  

select id from t where num=20 

 

15.in 和 not in 也要慎用,否则会导致全表扫描,如:  

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

对于连续的数值,能用 between 就不要用 in 了:

select id from t where num between 1 and 3

 

16.下面的查询也将导致全表扫描:

select id from t where name like '%abc%'  

 

17.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:

select id from t where num/2=100  

应改为: 

select id from t where num=100*2  

 

18.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:

select id from t where substring(name,1,3)='abc'--name以abc开头的id

应改为: 

select id from t where name like 'abc%'   

 

19.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。   

 

20.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,

否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。   

 

21.不要写一些没有意义的查询,如需要生成一个空表结构: 

select col1,col2 into #t from t where 1=0 

这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:  

create table #t(...)  

 

22.很多时候用 exists 代替 in 是一个好的选择:   

select num from a where num in(select num from b)

用下面的语句替换:

select num from a where exists(select 1 from b where num=a.num)

 

23.并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,  

如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。

 

24.索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,   

因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。   

一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。 

 

25.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。

这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。  

 

26.尽可能的使用 varchar 代替 char ,因为首先变长字段存储空间小,可以节省存储空间, 

其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。

 

27.任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。

 

28.避免频繁创建和删除临时表,以减少系统表资源的消耗。 

 

29.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,

以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。 

 

30.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。

31.使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。   

32.尽量避免大事务操作,提高系统并发能力。

 

33.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。

 

推荐阅读