sql性能优化

优化的主要切入点有:

①避免扫描全表

②减少sql解析步骤


  

一、避免扫描全表

    1.尽量在where 及 order by 涉及的列上建立索引。

    2.尽量避免用is null 或is not null来判断字段,因为不走索引。参考方案是设置字段NOT NULL,或设置默认值。
    3.避免在 where 子句中使用!=或<>操作符,因为不走索引。 MySQL只有对以下操作符才使用索引:<,<=,=,>,>=,BETWEEN,IN,以及某些时候的LIKE。

    4.下面的查询也将导致全表扫描:select id from t where name like ‘%abc%’ 或者select id from t where name like ‘%abc’若要提高效率,可以考虑全文检索。而select id from t where name like ‘abc%’ 才用到索引

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

select id from t where num/2=100

 

select id from t where substring(name,1,3)='abc'

    6.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。

    7.并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。

    8.尽量不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。不过个人认为,使用*可以减少表结构变更带来的代码维护量,修改字段时,无需修改代码中的sql。

    9.使用别名,可以提高sql解析速度。

    10.索引的使用规范:索引的创建要与应用结合考虑,建议大的OLTP表不要超过6个索引;

          尽可能的使用索引字段作为查询条件,尤其是聚簇索引,必要时可以通过index index_name来强制指定索引;

          避免对大表查询时进行table scan,必要时考虑新建索引;

          在使用索引字段作为条件时,如果该索引是联合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用 该索引,否则该索引将不会被使用;

          要注意索引的维护,周期性重建索引,重新编译存储过程。 

    11.使用 or 来连接条件这块有争议, 个人认为常规情况使用or即可,复杂情况可以考虑使用union

    12. 使用in 和not in这块有争议,个人认为 性能上使用exists 和not exists>=in 或not in,倾向使用exists。

13. 只查询一条数据的时候,可以使用limit 1,避免全表扫描

 

 

  ----------------------下面为纯转载---------------------

作者:面向大佬编程中
链接:https://zhuanlan.zhihu.com/p/48385127
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

   11.避免使用参数,会导致全表扫描。

37,避免使用临时表,除非却有需要,否则应尽量避免使用临时表,相反,可以使用表变量代替;大多数时候(99%),表变量驻扎在内存中,因此速度比临时表更快,临时表驻扎在TempDb数据库中,因此临时表上的操作需要跨数据库通信,速度自然慢。

38,最好不要使用触发器,触发一个触发器,执行一个触发器事件本身就是一个耗费资源的过程;如果能够使用约束实现的,尽量不要使用触发器;不要为不同的触发事件(Insert,Update和Delete)使用相同的触发器;不要在触发器中使用事务型代码。

39,索引创建规则:
表的主键、外键必须有索引;
数据量超过300的表应该有索引;
经常与其他表进行连接的表,在连接字段上应该建立索引;
经常出现在Where子句中的字段,特别是大表的字段,应该建立索引;
索引应该建在选择性高的字段上;
索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引;
复合索引的建立需要进行仔细分析,尽量考虑用单字段索引代替;
正确选择复合索引中的主列字段,一般是选择性较好的字段;
复合索引的几个字段是否经常同时以AND方式出现在Where子句中?单字段查询是否极少甚至没有?如果是,则可以建立复合索引;否则考虑单字段索引;
如果复合索引中包含的字段经常单独出现在Where子句中,则分解为多个单字段索引;
如果复合索引所包含的字段超过3个,那么仔细考虑其必要性,考虑减少复合的字段;
如果既有单字段索引,又有这几个字段上的复合索引,一般可以删除复合索引;
频繁进行数据操作的表,不要建立太多的索引;
删除无用的索引,避免对执行计划造成负面影响;
表上建立的每个索引都会增加存储开销,索引对于插入、删除、更新操作也会增加处理上的开销。另外,过多的复合索引,在有单字段索引的情况下,一般都是没有存在价值的;相反,还会降低数据增加删除时的性能,特别是对频繁更新的表来说,负面影响更大。
尽量不要对数据库中某个含有大量重复的值的字段建立索引。
 
 

40,mysql查询优化总结:使用慢查询日志去发现慢查询,使用执行计划去判断查询是否正常运行,总是去测试你的查询看看是否他们运行在最佳状态下。久而久之性能总会变化,避免在整个表上使用count(*),它可能锁住整张表,使查询保持一致以便后续相似的查询可以使用查询缓存
,在适当的情形下使用GROUP BY而不是DISTINCT,在WHERE, GROUP BY和ORDER BY子句中使用有索引的列,保持索引简单,不在多个索引中包含同一个列,有时候MySQL会使用错误的索引,对于这种情况使用USE INDEX,检查使用SQL_MODE=STRICT的问题,对于记录数小于5的索引字段,在UNION的时候使用LIMIT不是是用OR。
为了 避免在更新前SELECT,使用INSERT ON DUPLICATE KEY或者INSERT IGNORE ,不要用UPDATE去实现,不要使用 MAX,使用索引字段和ORDER BY子句,LIMIT M,N实际上可以减缓查询在某些情况下,有节制地使用,在WHERE子句中使用UNION代替子查询,在重新启动的MySQL,记得来温暖你的数据库,以确保您的数据在内存和查询速度快,考虑持久连接,而不是多个连接,以减少开销,基准查询,包括使用服务器上的负载,有时一个简单的查询可以影响其他查询,当负载增加您的服务器上,使用SHOW PROCESSLIST查看慢的和有问题的查询,在开发环境中产生的镜像数据中 测试的所有可疑的查询。

41,MySQL 备份过程:
从二级复制服务器上进行备份。在进行备份期间停止复制,以避免在数据依赖和外键约束上出现不一致。彻底停止MySQL,从数据库文件进行备份。
如果使用 MySQL dump进行备份,请同时备份二进制日志文件 – 确保复制没有中断。不要信任LVM 快照,这很可能产生数据不一致,将来会给你带来麻烦。为了更容易进行单表恢复,以表为单位导出数据 – 如果数据是与其他表隔离的。
当使用mysqldump时请使用 –opt。在备份之前检查和优化表。为了更快的进行导入,在导入时临时禁用外键约束。
为了更快的进行导入,在导入时临时禁用唯一性检测。在每一次备份后计算数据库,表以及索引的尺寸,以便更够监控数据尺寸的增长。
通过自动调度脚本监控复制实例的错误和延迟。定期执行备份。

42,查询缓冲并不自动处理空格,因此,在写SQL语句时,应尽量减少空格的使用,尤其是在SQL首和尾的空格(因为,查询缓冲并不自动截取首尾空格)。

43,member用mid做標準進行分表方便查询么?一般的业务需求中基本上都是以username为查询依据,正常应当是username做hash取模来分表吧。分表的话 mysql 的partition功能就是干这个的,对代码是透明的;
在代码层面去实现貌似是不合理的。

44,我们应该为数据库里的每张表都设置一个ID做为其主键,而且最好的是一个INT型的(推荐使用UNSIGNED),并设置上自动增加的AUTO_INCREMENT标志。

45,在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。
无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。

46,MySQL查询可以启用高速查询缓存。这是提高数据库性能的有效Mysql优化方法之一。当同一个查询被执行多次时,从缓存中提取数据和直接从数据库中返回数据快很多。

47,EXPLAIN SELECT 查询用来跟踪查看效果
使用 EXPLAIN 关键字可以让你知道MySQL是如何处理你的SQL语句的。这可以帮你分析你的查询语句或是表结构的性能瓶颈。EXPLAIN 的查询结果还会告诉你你的索引主键被如何利用的,你的数据表是如何被搜索和排序的……等等,等等。

48,当只要一行数据时使用 LIMIT 1
当你查询表的有些时候,你已经知道结果只会有一条结果,但因为你可能需要去fetch游标,或是你也许会去检查返回的记录数。在这种情况下,加上 LIMIT 1 可以增加性能。这样一样,MySQL数据库引擎会在找到一条数据后停止搜索,而不是继续往后查少下一条符合记录的数据。

49,选择表合适存储引擎:
myisam: 应用时以读和插入操作为主,只有少量的更新和删除,并且对事务的完整性,并发性要求不是很高的。
Innodb: 事务处理,以及并发条件下要求数据的一致性。除了插入和查询外,包括很多的更新和删除。(Innodb有效地降低删除和更新导致的锁定)。对于支持事务的InnoDB类型的表来说,影响速度的主要原因是AUTOCOMMIT默认设置是打开的,而且程序没有显式调用BEGIN 开始事务,导致每插入一条都自动提交,严重影响了速度。可以在执行sql前调用begin,多条sql形成一个事物(即使autocommit打开也可以),将大大提高性能。

50,优化表的数据类型,选择合适的数据类型:
原则:更小通常更好,简单就好,所有字段都得有默认值,尽量避免null。
例如:数据库表设计时候更小的占磁盘空间尽可能使用更小的整数类型.(mediumint就比int更合适)
比如时间字段:datetime和timestamp, datetime占用8个字节,而timestamp占用4个字节,只用了一半,而timestamp表示的范围是1970—2037适合做更新时间
MySQL可以很好的支持大数据量的存取,但是一般说来,数据库中的表越小,在它上面执行的查询也就会越快。
因此,在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设得尽可能小。例如,
在定义邮政编码这个字段时,如果将其设置为CHAR(255),显然给数据库增加了不必要的空间,
甚至使用VARCHAR这种类型也是多余的,因为CHAR(6)就可以很好的完成任务了。同样的,如果可以的话,
我们应该使用MEDIUMINT而不是BIGIN来定义整型字段。
应该尽量把字段设置为NOT NULL,这样在将来执行查询的时候,数据库不用去比较NULL值。
对于某些文本字段,例如“省份”或者“性别”,我们可以将它们定义为ENUM类型。因为在MySQL中,ENUM类型被当作数值型数据来处理,
而数值型数据被处理起来的速度要比文本类型快得多。这样,我们又可以提高数据库的性能。

51, 字符串数据类型:char,varchar,text选择区别

52,任何对列的操作都将导致表扫描,它包括数据库函数、计算表达式等等,查询时要尽可能将操作移至等号右边。

 

  

1 性能优化 1.1 避免频繁 commit,尤其是把 commit 写在循环体中每次循环都进行commit。 1.2 使用绑定变量,避免常量的直接引用。 示例:以下书写不符合本规范。 INSERT INTO sm_users (user_id, user_name, created_by, creation_date) VALUES (1, 'Tang', -1, SYSDATE); 建议用如下方式操作: DECLARE v_user_id sm_users.user_id%TYPE; v_user_name sm_users_user_name%TYPE; v_created_by sm_users.created_by%TYPE; v_creation_date sm_users.creation_date%TYPE; BEGIN ... INSERT INTO sm_users (user_id, user_name, created_by, creation_date) VALUES (v_user_id, v_user_name, v_created_by, v_creation_date); END; 1.3 Operator 的使用规范  IN  比较容易写及清晰易懂  但效能是比较低的  ORACLE试图将其转换成多个表的连接,如果转换不成功则先执行IN里面的子查询,再查询外层的表记录,如果转换成功则直接采用多个表的连接方式查询。由此可见用IN的SQL至少多了一个转换的过程。一般的SQL都可以转换成功,但对于含有分组统计等方面的SQL就不能转换了。  NOT IN  此操作是强列推荐不使用的,因为不能应用表的索引。  推荐方案:用NOT EXISTS 或(Outer-Join+判断为空)方案代替 例如: SELECT deptno FROM dept WHERE deptno NOT IN(SELECT deptno FROM emp) 建议写成: SELECT deptno FROM dept, emp WHERE dept.deptno = emp.deptno(+) AND emp.deptno IS NULL  <>  永远不会用到索引的  推荐方案:用其它相同功能的操作运算代替,如: a<>0 改为 a>0 or a<0 a<>’’ 改为 a>’’  IS NULL 或IS NOT NULL  一般是不会应用索引的,因为B-tree索引是不索引空值的。  推荐方案:用其它相同功能的操作运算代替,如: a is not null 改为 a>0 或a>’’  不允许字段为空,而用一个default代替空值,如业扩申请中状态区位不允许为空, default为申请。  > 及 <  有索引就会采用索引查找  但有的情况下可以对它进行优化  如一个表有100万记录,一个数值型字段A,30万记录的A=0,30万记录的A=1,39万记录的A=2,1万记录的A=3。那么执行A>2与A>=3的效果就有很大的区别了,因为A>2时ORACLE会先找出为2的记录索引再进行比较,而A>=3时ORACLE则直接找到=3的记录索引。  LIKE  LIKE可以应用通配符查询,里面的通配符组合可能达到几乎是任意的查询,但是如果用得不好则会产生性能上的问题,如LIKE ‘%5400%’ 这种查询不会引用索引,而LIKE ‘X5400%’则会引用范围索引。性能肯定大大提高。  UNION  SQL在运行时先取出数个查询的结果,再用排序空间进行排序删除重复的记录,最后返回结果集,如果表数据量大的话可能会导致用磁盘进行排序。  实际大部分应用中是不会产生重复的记录,推荐采用UNION ALL操作符替代UNION,因为UNION ALL操作只是简单的将两个结果合并后就返回。  Exists 示例:当有 A、B 两个结果集,当结果集 B 很大时,A 较小时,适用 exists,如: SELECT * FROM a WHERE EXISTS(SELECT 1 FROM b WHERE a.COLUMN = b.COLUMN); 当结果集 A 很大时,B 很小时,适用 in,如: SELECT * FROM a WHERE a.COLUMN IN(SELECT b.COLUMN FROM b) 1.4 SQL书写的影响  同一功能同一性能不同写法SQL的影响  Select * from zl_yhjbqk  Select * from dlyx.zl_yhjbqk(带表所有者的前缀)  Select * from DLYX.ZL_YHJBQK(大写表名)  Select * from DLYX.ZL_YHJBQK(中间多了空格)  以上四个SQL在ORACLE分析整理之后产生的结果及执行的时间是一样的,但是从ORACLE共享内存SGA的原理,可以得出ORACLE对每个SQL 都会对其进行一次分析,并且占用共享内存,如果将SQL的字符串及格式写得完全相同则ORACLE只会分析一次,共享内存也只会留下一次的分析结果,这不仅可以减少分析SQL的时间,而且可以减少共享内存重复的信息,ORACLE也可以准确统计SQL的执行频率。  WHERE后面的条件顺序影响  Select * from zl_yhjbqk where dy_dj = '1KV以下' and xh_bz=1  Select * from zl_yhjbqk where xh_bz=1 and dy_dj = '1KV以下'  以上两个SQL中dy_dj(电压等级)及xh_bz(销户标志)两个字段都没进行索引,所以执行的时候都是全表扫描,第一条SQL的dy_dj = '1KV以下'条件在记录集内比率为99%,而xh_bz=1的比率只为0.5%,在进行第一条SQL的时候99%条记录都进行dy_dj及xh_bz的比较,而在进行第二条SQL的时候0.5%条记录都进行dy_dj及xh_bz的比较,以此可以得出第二条SQL的CPU占用率明显比第一条低。  查询表顺序的影响  在FROM后面的表中的列表顺序会对SQL执行性能影响,在没有索引及ORACLE没有对表进行统计分析的情况下ORACLE会按表出现的顺序进行链接,由此因为表的顺序不对会产生十分耗服务器资源的数据交叉。(注:如果对表进行了统计分析,ORACLE会自动先进小表的链接,再进行大表的链接)  对条件字段的一些优化  采用函数处理的字段不能利用索引,如: substr(hbs_bh,1,4)=’5400’,优化处理:hbs_bh like ‘5400%’ trunc(sk_rq)=trunc(sysdate), 优化处理: sk_rq>=trunc(sysdate) and sk_rq<trunc(sysdate+1)  进行了显式或隐式的运算的字段不能进行索引,如: ss_df+20>50,优化处理:ss_df>30 ‘X’||hbs_bh>’X5400021452’,优化处理:hbs_bh>’5400021542’ sk_rq+5=sysdate,优化处理:sk_rq=sysdate-5 hbs_bh=5401002554,优化处理:hbs_bh=’ 5401002554’ 注:此条件对hbs_bh 进行隐式的to_number转换,因为hbs_bh字段是字符型  条件内包括了多个本表的字段运算时不能进行索引,如: ys_df>cx_df,无法进行优化 qc_bh||kh_bh=’5400250000’,优化处理:qc_bh=’5400’ and kh_bh=’250000’  HINT  是在ORACLE产生的SQL分析执行路径不满意的情况下要用到的。它可以对SQL进行以下方面的提示  目标方面的提示:  COST(按成本优化)  RULE(按规则优化)  CHOOSE(缺省)(ORACLE自动选择成本或规则进行优化)  ALL_ROWS(所有的行尽快返回)  FIRST_ROWS(第一行资料尽快返回)  执行方法的提示:  USE_NL(使用NESTED LOOPS方式联合)  USE_MERGE(使用MERGE JOIN方式联合)  USE_HASH(使用HASH JOIN方式联合)  索引提示:  INDEX(TABLE INDEX)(使用提示的表索引进行查询)  其它高级提示(如并行处理等等) 1.5 索引的规则: 建立索引常用的原则如下: 1. 表的主键、外键必须有索引 2. 数据量超过 1000 行的表应该有索引 3. 经常与其它表进行连接的表,在边接字段上应建立索引 4. 经常出现在 where 子句中的字段且过滤性极强的,特别是大表的字段,应该建立索引 5. 索引字段,尽量避免值为 null 6. 复合索引的建立需要仔细分析;尽量考虑用单字段索引代替:  正确选择复合索引中的第一个字段,一般是选择性较好的且在 where 子句中常的字段上。  复合索引的几个字段是否经常同时以and方式出现在where子句中?单字段查询是否极少甚至没有?如果是,则可以建立复合索引;否则考虑单字段索引。  如果复合索引中包含的字段经常单独出现在 where 子句中,则分解为多个单字段索引。  如果复合索引所包含的字段超过 3 个,那么仔细考虑其必要性,考虑减少复合的字段。  如果既有单字段索引,又有这几个字段上的复合索引,一般可以删除复合索引; 7. 频繁 DDL 的表,不要建立太多的索引 8. 删除无用的索引,避免对执行计划造成负面影响 9. 让 SQL 语句用上合理的索引,合理让 SQL 语句使用索引的原则如下:  首先,看是否用上了索引,对于该使用索引而没有用上索引的 SQL 语句,应该想办法用上索引。  其次,看是否用上正确的索引了,特别复杂的 SQL 语句,当其中 where 子句包含多个带有索引的字段时,更应该注意索引的选择是否合理。错误的索引不仅不会带来性能的提高,相反往往导致性能的降低。  针对如何用上合理的索引,以 Oracle 数据中的例子进行说明:  任何对列的操作都可能导致全表扫描,这里所谓的操作包括数据库函数、计算表达式等等,查询时要尽可能将操作移至等式的右边,甚至去掉函数。  避免不必要的类型转换,要了解“隐藏”的类型转换。  增加查询的范围,限制全范围的搜索。  索引选择性低,但资料分布差异很大时,仍然可以利用索引提高效率。  Oracle 优化器无法用上合理索引的情况下,利用 hint 强制指定索引。  使用复合索引且第一个索引字段没有出现在 where 中时,建议使用 hint 强制。 1.6 索引使用优化  建立Plan_Table CREATE TABLE PLAN_TABLE ( STATEMENT_ID VARCHAR2(30), TIMESTAMP DATE, REMARKS VARCHAR2(80), OPERATION VARCHAR2(30), OPTIONS VARCHAR2(30), OBJECT_NODE VARCHAR2(128), OBJECT_OWNER VARCHAR2(30), OBJECT_NAME VARCHAR2(30), OBJECT_INSTANCE NUMBER(38), OBJECT_TYPE VARCHAR2(30), OPTIMIZER VARCHAR2(255), SEARCH_COLUMNS NUMBER(38), ID NUMBER(38), PARENT_ID NUMBER(38), POSITION NUMBER(38), OTHER LONG )  Syntax 说明: explain plan set statement_id = user_define for select ... 将结果显示 SELECT LPAD(' ', 2 *(LEVEL - 1)) || operation op, options, object_name, POSITION FROM plan_table START WITH ID = 0 AND STATEMENT_ID = user_define CONNECT BY PRIOR ID = parent_id AND STATEMENT_ID = user_define  示例 如要测试下面SQL: SELECT c.short, a.cday, a.card_no, a.qty FROM sales.stockiohis a, sales.product_info b, sales.vendor c WHERE a.card_no = b.card_no AND b.vendorid = c.vendorid AND a.produce_no = '2007090001' AND a.CATEGORY = '10' AND a.iotype = '1' 新增文件:例 d:\mydoc\plan.sql '0001'为user_define为使用者自定义编号 EXPLAIN PLAN SET STATEMENT_ID = '0001' FOR SELECT 'X' FROM sales.stockiohis a ,sales.product_info b ,sales.vendor c WHERE a.card_no = b.card_no AND b.vendorid = c.vendorid AND a.produce_no = '2007090001' AND a.CATEGORY = '10' AND a.iotype = '1' / SET arraysize 1 SET line 100 COLUMN op format a40 COLUMN object_name format a20 COLUMN options format a20 SELECT LPAD(' ', 2 *(LEVEL - 1)) || operation op, options, object_name, POSITION FROM plan_table START WITH ID = 0 AND STATEMENT_ID = '0001' CONNECT BY PRIOR ID = parent_id AND STATEMENT_ID = '0001' / DELETE FROM plan_table WHERE STATEMENT_ID = '0001' / COMMIT / 结果 1.7 避免不必要的排序 说明:对查询结果进行排序会大大的降低系统的性能,group与union都会对数据作排序,要耗费较多的内存,视状况用union all既可,不然有时数据太大又要进行union的排序,会导致Oracle数据库SORT_AREA_SIZE不足发生系统错误。 1.8 对于数字型的Primary Key,建议用序列 sequence 产生。 说明:除非是单据的单号,要求必须是唯一,并且依据流水号不可以跳号,不然在大量交易的表格中,不在乎跳耗时,要取得唯一的Primary Key 建议使用Oracle Sequence这样速度会较快,而且不会有锁定(Lock)的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值