搜文章
推荐 原创 视频 Java开发 iOS开发 前端开发 JavaScript开发 Android开发 PHP开发 数据库 开发工具 Python开发 Kotlin开发 Ruby开发 .NET开发 服务器运维 开放平台 架构师 大数据 云计算 人工智能 开发语言 其它开发
Lambda在线 > LionHeart心有猛虎细嗅蔷薇 > 第十四更——数据库性能优化详解

第十四更——数据库性能优化详解

LionHeart心有猛虎细嗅蔷薇 2017-11-28

SQL性能优化  --- 面试题

今天面试,我简历上写了熟悉sql的性能优化,但是今天面试,一时想不起别的,就仅仅说出了一条,在这里再总结一些,完善自己的知识点。

我经常用的数据库是oracle,所以我的sql优化是程序员针对于oracle的。

总结,这个sql优化是针对程序员的,而不是针对dba的,主要就是第一,尽量防止模糊,明确指出,即用列名代替*,第二,在where语句上下工夫。第三多表查询和子查询,第四尽量使用绑定。

 

 

数据库性能优化之SQL语句优化1

一、问题的提出

在应用系统开发初期,由于开发数据库数据比较少,对于查询SQL语句,复杂视图的的编写等体会不出SQL语句各种写法的性能优劣,但是如果将应用系统提交实际应用后,随着数据库中数据的增加,系统的响应速度就成为目前系统需要解决的最主要的问题之一。系统优化中一个很重要的方面就是SQL语句的优化。对于海量数据,劣质SQL语句和优质SQL语句之间的速度差别可以达到上百倍,可见对于一个系统不是简单地能实现其功能就可,而是要写出高质量的SQL语句,提高系统的可用性。

在多数情况下,Oracle使用索引来更快地遍历表,优化器主要根据定义的索引来提高性能。但是,如果在SQL语句的where子句中写的SQL代码不合理,就会造成优化器删去索引而使用全表扫描,一般就这种SQL语句就是所谓的劣质SQL语句。在编写SQL语句时我们应清楚优化器根据何种原则来删除索引,这有助于写出高性能的SQL语句。

二、SQL语句编写注意问题

下面就某些SQL语句的where子句编写中需要注意的问题作详细介绍。在这些where子句中,即使某些列存在索引,但是由于编写了劣质的SQL,系统在运行该SQL语句时也不能使用该索引,而同样使用全表扫描,这就造成了响应速度的极大降低。

1. 操作符优化

(a) IN 操作符

用IN写出来的SQL的优点是比较容易写及清晰易懂,这比较适合现代软件开发的风格。但是用IN的SQL性能总是比较低的,从Oracle执行的步骤来分析用IN的SQL与不用IN的SQL有以下区别:

ORACLE试图将其转换成多个表的连接,如果转换不成功则先执行IN里面的子查询,再查询外层的表记录,如果转换成功则直接采用多个表的连接方式查询。由此可见用IN的SQL至少多了一个转换的过程。一般的SQL都可以转换成功,但对于含有分组统计等方面的SQL就不能转换了。

推荐方案:在业务密集的SQL当中尽量不采用IN操作符,用EXISTS 方案代替。

(b) NOT IN操作符

此操作是强列不推荐使用的,因为它不能应用表的索引。

推荐方案:用NOT EXISTS 方案代替

(c) IS NULL 或IS NOT NULL操作(判断字段是否为空)

判断字段是否为空一般是不会应用索引的,因为索引是不索引空值的。不能用null作索引,任何包含null值的列都将不会被包含在索引中。即使索引有多列这样的情况下,只要这些列中有一列含有null,该列就会从索引中排除。也就是说如果某列存在空值,即使对该列建索引也不会提高性能。任何在where子句中使用is null或isnot null的语句优化器是不允许使用索引的。

推荐方案:用其它相同功能的操作运算代替,如:a is not null 改为 a>0 或a>’’等。不允许字段为空,而用一个缺省值代替空值,如申请中状态字段不允许为空,缺省为申请。

(d) > 及 < 操作符(大于或小于操作符)

大于或小于操作符一般情况下是不用调整的,因为它有索引就会采用索引查找,但有的情况下可以对它进行优化,如一个表有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的记录索引。

(e) LIKE操作符

LIKE操作符可以应用通配符查询,里面的通配符组合可能达到几乎是任意的查询,但是如果用得不好则会产生性能上的问题,如LIKE ‘%5400%’ 这种查询不会引用索引,而LIKE ‘X5400%’则会引用范围索引。

一个实际例子:用YW_YHJBQK表中营业编号后面的户标识号可来查询营业编号 YY_BH LIKE ‘%5400%’ 这个条件会产生全表扫描,如果改成YY_BH LIKE ’X5400%’OR YY_BH LIKE ’B5400%’ 则会利用YY_BH的索引进行两个范围的查询,性能肯定大大提高。

带通配符(%)的like语句:

同样以上面的例子来看这种情况。目前的需求是这样的,要求在职工表中查询名字中包含cliton的人。可以采用如下的查询SQL语句:

select * fromemployee where last_name like '%cliton%';

这里由于通配符(%)在搜寻词首出现,所以Oracle系统不使用last_name的索引。在很多情况下可能无法避免这种情况,但是一定要心中有底,通配符如此使用会降低查询速度。然而当通配符出现在字符串其他位置时,优化器就能利用索引。在下面的查询中索引得到了使用:

select * fromemployee where last_name like 'c%';

(f) UNION操作符

UNION在进行表链接后会筛选掉重复的记录,所以在表链接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果。实际大部分应用中是不会产生重复的记录,最常见的是过程表与历史表UNION。如: 
select * from gc_dfys 
union 
select * from ls_jg_dfys 
这个SQL在运行时先取出两个表的结果,再用排序空间进行排序删除重复的记录,最后返回结果集,如果表数据量大的话可能会导致用磁盘进行排序。

推荐方案:采用UNION ALL操作符替代UNION,因为UNION ALL操作只是简单的将两个结果合并后就返回。

select * from gc_dfys 
union all 
select * from ls_jg_dfys

(g) 联接列

对于有联接的列,即使最后的联接值为一个静态值,优化器是不会使用索引的。我们一起来看一个例子,假定有一个职工表(employee),对于一个职工的姓和名分成两列存放(FIRST_NAME和LAST_NAME),现在要查询一个叫比尔.克林顿(Bill Cliton)的职工。

下面是一个采用联接查询的SQL语句:

select * fromemployss where first_name||''||last_name ='Beill Cliton';

上面这条语句完全可以查询出是否有BillCliton这个员工,但是这里需要注意,系统优化器对基于last_name创建的索引没有使用。当采用下面这种SQL语句的编写,Oracle系统就可以采用基于last_name创建的索引。

*** wherefirst_name ='Beill' and last_name ='Cliton';

(h) Order by语句

ORDER BY语句决定了Oracle如何将返回的查询结果排序。Order by语句对要排序的列没有什么特别的限制,也可以将函数加入列中(象联接或者附加等)。任何在Order by语句的非索引项或者有计算表达式都将降低查询速度。

仔细检查order by语句以找出非索引项或者表达式,它们会降低性能。解决这个问题的办法就是重写order by语句以使用索引,也可以为所使用的列建立另外一个索引,同时应绝对避免在orderby子句中使用表达式。

(i) NOT

我们在查询时经常在where子句使用一些逻辑表达式,如大于、小于、等于以及不等于等等,也可以使用and(与)、or(或)以及not(非)。NOT可用来对任何逻辑运算符号取反。下面是一个NOT子句的例子:

... where not(status ='VALID')

如果要使用NOT,则应在取反的短语前面加上括号,并在短语前面加上NOT运算符。NOT运算符包含在另外一个逻辑运算符中,这就是不等于(<>)运算符。换句话说,即使不在查询where子句中显式地加入NOT词,NOT仍在运算符中,见下例:

... where status<>'INVALID';

对这个查询,可以改写为不使用NOT:

select * fromemployee where salary<3000 or salary>3000;

虽然这两种查询的结果一样,但是第二种查询方案会比第一种查询方案更快些。第二种查询允许Oracle对salary列使用索引,而第一种查询则不能使用索引。

2. SQL书写的影响

(a) 同一功能同一性能不同写法SQL的影响。

如一个SQL在A程序员写的为  Select * from zl_yhjbqk

B程序员写的为 Select * fromdlyx.zl_yhjbqk(带表所有者的前缀)

C程序员写的为 Select * fromDLYX.ZLYHJBQK(大写表名)

D程序员写的为 Select*  from DLYX.ZLYHJBQK(中间多了空格)

以上四个SQL在ORACLE分析整理之后产生的结果及执行的时间是一样的,但是从ORACLE共享内存SGA的原理,可以得出ORACLE对每个SQL都会对其进行一次分析,并且占用共享内存,如果将SQL的字符串及格式写得完全相同,则ORACLE只会分析一次,共享内存也只会留下一次的分析结果,这不仅可以减少分析SQL的时间,而且可以减少共享内存重复的信息,ORACLE也可以准确统计SQL的执行频率。

(b) WHERE后面的条件顺序影响

WHERE子句后面的条件顺序对大数据量表的查询会产生直接的影响。如: 
Select * from zl_yhjbqk where dy_dj = '1KV以下' andxh_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占用率明显比第一条低。

(c) 查询表顺序的影响

在FROM后面的表中的列表顺序会对SQL执行性能影响,在没有索引及ORACLE没有对表进行统计分析的情况下,ORACLE会按表出现的顺序进行链接,由此可见表的顺序不对时会产生十分耗服物器资源的数据交叉。(注:如果对表进行了统计分析,ORACLE会自动先进小表的链接,再进行大表的链接)

3. SQL语句索引的利用

(a) 对条件字段的一些优化

采用函数处理的字段不能利用索引,如:

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’ andkh_bh=’250000’

4. 更多方面SQL优化资料分享

(1) 选择最有效率的表名顺序(只在基于规则的优化器中有效):

ORACLE 的解析器按照从右到左的顺序处理FROM子句中的表名,FROM子句中写在最后的表(基础表driving table)将被最先处理,在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表。如果有3个以上的表连接查询, 那就需要选择交叉表(intersection table)作为基础表, 交叉表是指那个被其他表所引用的表.

(2) WHERE子句中的连接顺序:

ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前, 那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾.

(3) SELECT子句中避免使用‘ * ‘:

ORACLE在解析的过程中, 会将'*' 依次转换成所有的列名, 这个工作是通过查询数据字典完成的, 这意味着将耗费更多的时间。

(4) 减少访问数据库的次数:

ORACLE在内部执行了许多工作: 解析SQL语句, 估算索引的利用率, 绑定变量 , 读数据块等。

(5) 在SQL*Plus , SQL*Forms和Pro*C中重新设置ARRAYSIZE参数, 可以增加每次数据库访问的检索数据量 ,建议值为200。

(6) 使用DECODE函数来减少处理时间:

使用DECODE函数可以避免重复扫描相同记录或重复连接相同的表.

(7) 整合简单,无关联的数据库访问:

如果你有几个简单的数据库查询语句,你可以把它们整合到一个查询中(即使它们之间没有关系) 。

(8) 删除重复记录:

最高效的删除重复记录方法 ( 因为使用了ROWID)例子: 
DELETE  FROM  EMP E  WHERE  E.ROWID > (SELECTMIN(X.ROWID) FROM  EMP X  WHERE  X.EMP_NO = E.EMP_NO)。

(9) 用TRUNCATE替代DELETE:

当删除表中的记录时,在通常情况下, 回滚段(rollback segments ) 用来存放可以被恢复的信息. 如果你没有COMMIT事务,ORACLE会将数据恢复到删除之前的状态(准确地说是恢复到执行删除命令之前的状况) 而当运用TRUNCATE时, 回滚段不再存放任何可被恢复的信息.当命令运行后,数据不能被恢复.因此很少的资源被调用,执行时间也会很短. (译者按:TRUNCATE只在删除全表适用,TRUNCATE是DDL不是DML) 。

(10) 尽量多使用COMMIT:

只要有可能,在程序中尽量多使用COMMIT,这样程序的性能得到提高,需求也会因为COMMIT所释放的资源而减少,COMMIT所释放的资源: 
a. 回滚段上用于恢复数据的信息. 
b. 被程序语句获得的锁 
c. redo log buffer 中的空间 
d. ORACLE为管理上述3种资源中的内部花费

(11) 用Where子句替换HAVING子句:

避免使用HAVING子句, HAVING 只会在检索出所有记录之后才对结果集进行过滤. 这个处理需要排序,总计等操作. 如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销. (非oracle中)on、where、having这三个都可以加条件的子句中,on是最先执行,where次之,having最后,因为on是先把不符合条件的记录过滤后才进行统计,它就可以减少中间运算要处理的数据,按理说应该速度是最快的,where也应该比having快点的,因为它过滤数据后才进行sum,在两个表联接时才用on的,所以在一个表的时候,就剩下where跟having比较了。在这单表查询统计的情况下,如果要过滤的条件没有涉及到要计算字段,那它们的结果是一样的,只是where可以使用rushmore技术,而having就不能,在速度上后者要慢如果要涉及到计算的字 段,就表示在没计算之前,这个字段的值是不确定的,根据上篇写的工作流程,where的作用时间是在计算之前就完成的,而having就是在计算后才起作用的,所以在这种情况下,两者的结果会不同。在多表联接查询时,on比where更早起作用。系统首先根据各个表之间的联接条件,把多个表合成一个临时表后,再由where进行过滤,然后再计算,计算完后再由having进行过滤。由此可见,要想过滤条件起到正确的作用,首先要明白这个条件应该在什么时候起作用,然后再决定放在那里。

(12) 减少对表的查询:

在含有子查询的SQL语句中,要特别注意减少对表的查询.例子: 
SELECT  TAB_NAME FROM TABLES WHERE (TAB_NAME,DB_VER) = ( SELECTTAB_NAME,DB_VER FROM  TAB_COLUMNS  WHERE  VERSION = 604)

(13) 通过内部函数提高SQL效率:

复杂的SQL往往牺牲了执行效率. 能够掌握上面的运用函数解决问题的方法在实际工作中是非常有意义的。

(14) 使用表的别名(Alias):

当在SQL语句中连接多个表时, 请使用表的别名并把别名前缀于每个Column上.这样一来,就可以减少解析的时间并减少那些由Column歧义引起的语法错误。

(15) 用EXISTS替代IN、用NOT EXISTS替代NOTIN:

在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接.在这种情况下, 使用EXISTS(或NOTEXISTS)通常将提高查询的效率. 在子查询中,NOT IN子句将执行一个内部的排序和合并. 无论在哪种情况下,NOT IN都是最低效的 (因为它对子查询中的表执行了一个全表遍历). 为了避免使用NOT IN ,我们可以把它改写成外连接(Outer Joins)或NOT EXISTS。 
例子: 
(高效)SELECT * FROM  EMP (基础表)  WHERE  EMPNO > 0  AND  EXISTS (SELECT ‘X'  FROM DEPT  WHERE  DEPT.DEPTNO = EMP.DEPTNO AND  LOC = ‘MELB') 
(低效)SELECT  * FROM  EMP (基础表)  WHERE  EMPNO > 0  AND  DEPTNO IN(SELECTDEPTNO  FROM  DEPT  WHERE  LOC = ‘MELB')

(16) 识别'低效执行'的SQL语句:

虽然目前各种关于SQL优化的图形化工具层出不穷,但是写出自己的SQL工具来解决问题始终是一个最好的方法: 
SELECT  EXECUTIONS , DISK_READS, BUFFER_GETS, 
ROUND((BUFFER_GETS-DISK_READS)/BUFFER_GETS,2) Hit_radio, 
ROUND(DISK_READS/EXECUTIONS,2) Reads_per_run, 
SQL_TEXT 
FROM  V$SQLAREA 
WHERE  EXECUTIONS>0 
AND  BUFFER_GETS > 0 
AND  (BUFFER_GETS-DISK_READS)/BUFFER_GETS < 0.8 
ORDER BY  4 DESC;

(17) 用索引提高效率:

索引是表的一个概念部分,用来提高检索数据的效率,ORACLE使用了一个复杂的自平衡B-tree结构. 通常,通过索引查询数据比全表扫描要快.当ORACLE找出执行查询和Update语句的最佳路径时, ORACLE优化器将使用索引. 同样在联结多个表时使用索引也可以提高效率. 另一个使用索引的好处是,它提供了主键(primarykey)的唯一性验证.。那些LONG或LONG RAW数据类型, 你可以索引几乎所有的列. 通常, 在大型表中使用索引特别有效.当然,你也会发现, 在扫描小表时,使用索引同样能提高效率. 虽然使用索引能得到查询效率的提高,但是我们也必须注意到它的代价. 索引需要空间来存储,也需要定期维护, 每当有记录在表中增减或索引列被修改时, 索引本身也会被修改. 这意味着每条记录的INSERT , DELETE , UPDATE将为此多付出4 , 5 次的磁盘I/O . 因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢.。定期的重构索引是有必要的: 
ALTER  INDEX <INDEXNAME> REBUILD <TABLESPACENAME>

(18) 用EXISTS替换DISTINCT:

当提交一个包含一对多表信息(比如部门表和雇员表)的查询时,避免在SELECT子句中使用DISTINCT. 一般可以考虑用EXIST替换, EXISTS 使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结果. 例子: 
(低效): 
SELECT  DISTINCT  DEPT_NO,DEPT_NAME  FROM  DEPT D , EMP EWHERE  D.DEPT_NO = E.DEPT_NO 
(高效): 
SELECT  DEPT_NO,DEPT_NAME  FROM  DEPT D  WHERE  EXISTS( SELECT ‘X'  FROM  EMP E  WHEREE.DEPT_NO = D.DEPT_NO);

(19) sql语句用大写的;因为oracle总是先解析sql语句,把小写的字母转换成大写的再执行。

(20) 在java代码中尽量少用连接符“+”连接字符串!

(21) 避免在索引列上使用NOT,通常我们要避免在索引列上使用NOT, NOT会产生在和在索引列上使用函数相同的影响. 当ORACLE”遇到”NOT,他就会停止使用索引转而执行全表扫描。

(22) 避免在索引列上使用计算 
WHERE子句中,如果索引列是函数的一部分.优化器将不使用索引而使用全表扫描.举例: 
低效: 
SELECT … FROM  DEPT  WHERE SAL * 12 >25000; 
高效: 
SELECT … FROM DEPT WHERE SAL > 25000/12;

(23) 用>=替代> 
高效: 
SELECT * FROM  EMP  WHERE  DEPTNO >=4 
低效: 
SELECT * FROM EMP WHERE DEPTNO >3 
两者的区别在于, 前者DBMS将直接跳到第一个DEPT等于4的记录而后者将首先定位到DEPTNO=3的记录并且向前扫描到第一个DEPT大于3的记录。

(24) 用UNION替换OR (适用于索引列)

通常情况下, 用UNION替换WHERE子句中的OR将会起到较好的效果.对索引列使用OR将造成全表扫描. 注意, 以上规则只针对多个索引列有效. 如果有column没有被索引, 查询效率可能会因为你没有选择OR而降低. 在下面的例子中,LOC_ID 和REGION上都建有索引. 
高效: 
SELECT LOC_ID , LOC_DESC , REGION 
FROM LOCATION 
WHERE LOC_ID = 10 
UNION 
SELECT LOC_ID , LOC_DESC , REGION 
FROM LOCATION 
WHERE REGION = “MELBOURNE” 
低效: 
SELECT LOC_ID , LOC_DESC , REGION 
FROM LOCATION 
WHERE LOC_ID = 10 OR REGION = “MELBOURNE” 
如果你坚持要用OR, 那就需要返回记录最少的索引列写在最前面.

(25) 用IN来替换OR

这是一条简单易记的规则,但是实际的执行效果还须检验,在ORACLE8i下,两者的执行路径似乎是相同的. 
低效: 
SELECT…. FROM LOCATION WHERE LOC_ID = 10 OR LOC_ID = 20OR LOC_ID = 30 
高效 
SELECT… FROM LOCATION WHERE LOC_IN  IN (10,20,30);

(26) 避免在索引列上使用ISNULL和IS NOT NULL

避免在索引中使用任何可以为空的列,ORACLE将无法使用该索引.对于单列索引,如果列包含空值,索引中将不存在此记录. 对于复合索引,如果每个列都为空,索引中同样不存在此记录. 如果至少有一个列不为空,则记录存在于索引中.举例: 如果唯一性索引建立在表的A列和B列上, 并且表中存在一条记录的A,B值为(123,null), ORACLE将不接受下一条具有相同A,B值(123,null)的记录(插入). 然而如果所有的索引列都为空,ORACLE将认为整个键值为空而空不等于空. 因此你可以插入1000 条具有相同键值的记录,当然它们都是空! 因为空值不存在于索引列中,所以WHERE子句中对索引列进行空值比较将使ORACLE停用该索引. 
低效: (索引失效) 
SELECT … FROM  DEPARTMENT  WHERE DEPT_CODE IS NOT NULL; 
高效: (索引有效) 
SELECT … FROM  DEPARTMENT  WHERE DEPT_CODE >=0;

(27) 总是使用索引的第一个列:

如果索引是建立在多个列上, 只有在它的第一个列(leading column)被where子句引用时,优化器才会选择使用该索引. 这也是一条简单而重要的规则,当仅引用索引的第二个列时,优化器使用了全表扫描而忽略了索引。

(28) 用UNION-ALL 替换UNION ( 如果有可能的话):

当SQL 语句需要UNION两个查询结果集合时,这两个结果集合会以UNION-ALL的方式被合并, 然后在输出最终结果前进行排序. 如果用UNION ALL替代UNION, 这样排序就不是必要了. 效率就会因此得到提高. 需要注意的是,UNION ALL 将重复输出两个结果集合中相同记录. 因此各位还是要从业务需求分析使用UNION ALL的可行性. UNION 将对结果集合排序,这个操作会使用到SORT_AREA_SIZE这块内存. 对于这块内存的优化也是相当重要的. 下面的SQL可以用来查询排序的消耗量 
低效: 
SELECT  ACCT_NUM, BALANCE_AMT 
FROM  DEBIT_TRANSACTIONS 
WHERE TRAN_DATE = '31-DEC-95' 
UNION 
SELECT ACCT_NUM, BALANCE_AMT 
FROM DEBIT_TRANSACTIONS 
WHERE TRAN_DATE = '31-DEC-95' 
高效: 
SELECT ACCT_NUM, BALANCE_AMT 
FROM DEBIT_TRANSACTIONS 
WHERE TRAN_DATE = '31-DEC-95' 
UNION ALL 
SELECT ACCT_NUM, BALANCE_AMT 
FROM DEBIT_TRANSACTIONS 
WHERE TRAN_DATE = '31-DEC-95'

(29) 用WHERE替代ORDER BY:

ORDER BY 子句只在两种严格的条件下使用索引. 
ORDER BY中所有的列必须包含在相同的索引中并保持在索引中的排列顺序. 
ORDER BY中所有的列必须定义为非空. 
WHERE子句使用的索引和ORDER BY子句中所使用的索引不能并列. 
例如: 
表DEPT包含以下列: 
DEPT_CODE PK NOT NULL 
DEPT_DESC NOT NULL 
DEPT_TYPE NULL 
低效: (索引不被使用) 
SELECT DEPT_CODE FROM  DEPT  ORDER BY  DEPT_TYPE 
高效: (使用索引) 
SELECT DEPT_CODE  FROM  DEPT  WHERE  DEPT_TYPE > 0

(30) 避免改变索引列的类型:

当比较不同数据类型的数据时, ORACLE自动对列进行简单的类型转换. 
假设 EMPNO是一个数值类型的索引列. 
SELECT …  FROM EMP  WHERE  EMPNO = ‘123' 
实际上,经过ORACLE类型转换, 语句转化为: 
SELECT …  FROM EMP  WHERE  EMPNO =TO_NUMBER(‘123') 
幸运的是,类型转换没有发生在索引列上,索引的用途没有被改变. 
现在,假设EMP_TYPE是一个字符类型的索引列. 
SELECT …  FROM EMP  WHERE EMP_TYPE =123 
这个语句被ORACLE转换为: 
SELECT …  FROM EMP  WHERETO_NUMBER(EMP_TYPE)=123 
因为内部发生的类型转换, 这个索引将不会被用到! 为了避免ORACLE对你的SQL进行隐式的类型转换, 最好把类型转换用显式表现出来. 注意当字符和数值比较时, ORACLE会优先转换数值类型到字符类型。

分析select  emp_name   form   employee   where  salary   >   3000   在此语句中若salary是Float类型的,则优化器对其进行优化为Convert(float,3000),因为3000是个整数,我们应在编程时使用3000.0而不要等运行时让DBMS进行转化。同样字符和整型数据的转换。


(31) 需要当心的WHERE子句:

某些SELECT 语句中的WHERE子句不使用索引. 这里有一些例子. 
在下面的例子里, (1)‘!=' 将不使用索引. 记住, 索引只能告诉你什么存在于表中,而不能告诉你什么不存在于表中. (2) ‘ ¦ ¦'是字符连接函数. 就象其他函数那样, 停用了索引.(3) ‘+'是数学函数. 就象其他数学函数那样, 停用了索引. (4)相同的索引列不能互相比较,这将会启用全表扫描.

(32) a. 如果检索数据量超过30%的表中记录数.使用索引将没有显著的效率提高. b. 在特定情况下, 使用索引也许会比全表扫描慢, 但这是同一个数量级上的区别. 而通常情况下,使用索引比全表扫描要块几倍乃至几千倍!

(33) 避免使用耗费资源的操作:

带有DISTINCT,UNION,MINUS,INTERSECT,ORDERBY的SQL语句会启动SQL引擎执行耗费资源的排序(SORT)功能. DISTINCT需要一次排序操作, 而其他的至少需要执行两次排序. 通常,带有UNION, MINUS , INTERSECT的SQL语句都可以用其他方式重写. 如果你的数据库的SORT_AREA_SIZE调配得好, 使用UNION , MINUS, INTERSECT也是可以考虑的, 毕竟它们的可读性很强。

(34) 优化GROUP BY:

提高GROUP BY 语句的效率,可以通过将不需要的记录在GROUP BY 之前过滤掉.下面两个查询返回相同结果但第二个明显就快了许多. 
低效: 
SELECT JOB , AVG(SAL) 
FROM EMP 
GROUP by JOB 
HAVING JOB = ‘PRESIDENT' 
OR JOB = ‘MANAGER' 
高效: 
SELECT JOB , AVG(SAL) 
FROM EMP 
WHERE JOB = ‘PRESIDENT' 
OR JOB = ‘MANAGER' 
GROUP by JOB

 

另有参考 http://my.oschina.net/xianggao/blog/87448 数据库性能优化之SQL语句优化2

http://my.oschina.net/xianggao/blog/87450 数据库性能优化之SQL语句优化3

http://my.oschina.net/xianggao/blog/87453 数据库性能优化之SQL语句优化4

http://my.oschina.net/xianggao/blog/87223  关于如何形成一个好的数据库设计

SQL性能优化二

·  优化目标

  1. 减少 IO 次数
        IO
    永远是数据库最容易瓶颈的地方,这是由数据库的职责所决定的,大部分数据库操作中超过90%的时间都是     IO 操作所占用的,减少     IO 次数是     SQL 优化中需要第一优先考虑,当然,也是收效最明显的优化手段。

  2. 降低 CPU 计算
       
    除了     IO 瓶颈之外,SQL优化中需要考虑的就是     CPU 运算量的优化了。order     by, group by,distinct … 都是消耗 CPU 的大户(这些操作基本上都是     CPU 处理内存中的数据比较运算)。当我们的     IO 优化做到一定阶段之后,降低     CPU 计算也就成为了我们     SQL 优化的重要目标

·  优化方法

  1. 改变 SQL 执行计划
       
    明确了优化目标之后,我们需要确定达到我们目标的方法。对于     SQL 语句来说,达到上述2个目标的方法其实只有一个,那就是改变     SQL 的执行计划,让他尽量少走弯路,尽量通过各种捷径来找到我们需要的数据,以达到     “减少     IO 次数”         “降低     CPU 计算”     的目标

·  常见误区

  1. count(1)count(primary_key)     优于     count(*)
       
    很多人为了统计记录条数,就使用     count(1)     count(primary_key) 而不是 count(*) ,他们认为这样性能更好,其实这是一个误区。对于有些场景,这样做可能性能会更差,应为数据库对     count(*) 计数操作做了一些特别的优化。

  2. count(column)         count(*) 是一样的
       
    这个误区甚至在很多的资深工程师或者是     DBA 中都普遍存在,很多人都会认为这是理所当然的。实际上,count(column)         count(*) 是一个完全不一样的操作,所代表的意义也完全不一样。
        count(column)
    是表示结果集中有多少个column字段不为空的记录
        count(*)
    是表示整个结果集有多少条记录

  3. select     a,b from …     select a,b,c from … 可以让数据库访问更少的数据量
       
    这个误区主要存在于大量的开发人员中,主要原因是对数据库的存储原理不是太了解。
       
    实际上,大多数关系型数据库都是按照行(row)的方式存储,而数据存取操作都是以一个固定大小的IO单元(被称作     block 或者     page)为单位,一般为4KB8KB…     大多数时候,每个IO单元中存储了多行,每行都是存储了该行的所有字段(lob等特殊类型字段除外)。
       
    所以,我们是取一个字段还是多个字段,实际上数据库在表中需要访问的数据量其实是一样的。
       
    当然,也有例外情况,那就是我们的这个查询在索引中就可以完成,也就是说当只取     a,b两个字段的时候,不需要回表,而c这个字段不在使用的索引中,需要回表取得其数据。在这样的情况下,二者的IO量会有较大差异。

  4. order     by 一定需要排序操作
       
    我们知道索引数据实际上是有序的,如果我们的需要的数据和某个索引的顺序一致,而且我们的查询又通过这个索引来执行,那么数据库一般会省略排序操作,而直接将数据返回,因为数据库知道数据已经满足我们的排序需求了。
       
    实际上,利用索引来优化有排序需求的     SQL,是一个非常重要的优化手段
       
    延伸阅读:MySQL ORDER BY 的实现分析 MySQL GROUP BY 基本实现原理 以及 MySQL DISTINCT 的基本实现原理 3篇文章中有更为深入的分析,尤其是第一篇

  5. 执行计划中有     filesort 就会进行磁盘文件排序
       
    有这个误区其实并不能怪我们,而是因为     MySQL 开发者在用词方面的问题。filesort     是我们在使用     explain 命令查看一条     SQL 的执行计划的时候可能会看到在     “Extra” 一列显示的信息。
       
    实际上,只要一条     SQL 语句需要进行排序操作,都会显示“Using     filesort”,这并不表示就会有文件排序操作。
       
    延伸阅读:理解 MySQL Explain 命令输出中的filesort,我在这里有更为详细的介绍

·  基本原则

  1. 尽量少 join
        MySQL 的优势在于简单,但这在某些方面其实也是其劣势。MySQL 优化器效率高,但是由于其统计信息的量有限,优化器工作过程出现偏差的可能性也就更多。对于复杂的多表 Join,一方面由于其优化器受限,再者在 Join 这方面所下的功夫还不够,所以性能表现离 Oracle 等关系型数据库前辈还是有一定距离。但如果是简单的单表查询,这一差距就会极小甚至在有些场景下要优于这些数据库前辈。

  2. 尽量少排序
        排序操作会消耗较多的 CPU 资源,所以减少排序可以在缓存命中率高等 IO 能力足够的场景下会较大影响 SQL 的响应时间。
        对于MySQL来说,减少排序有多种办法,比如:

    • 上面误区中提到的通过利用索引来排序的方式进行优化

    • 减少参与排序的记录条数

    • 非必要不对数据进行排序

  3. 尽量避免     select *
        很多人看到这一点后觉得比较难理解,上面不是在误区中刚刚说 select 子句中字段的多少并不会影响到读取的数据吗?
        是的,大多数时候并不会影响到 IO 量,但是当我们还存在 order by 操作的时候,select 子句中的字段多少会在很大程度上影响到我们的排序效率,这一点可以通过我之前一篇介绍 MySQL ORDER BY 的实现分析 的文章中有较为详细的介绍。
        此外,上面误区中不是也说了,只是大多数时候是不会影响到 IO 量,当我们的查询结果仅仅只需要在索引中就能找到的时候,还是会极大减少 IO 量的。

  4. 尽量用 join 代替子查询
        虽然 Join 性能并不佳,但是和 MySQL 的子查询比起来还是有非常大的性能优势。MySQL 的子查询执行计划一直存在较大的问题,虽然这个问题已经存在多年,但是到目前已经发布的所有稳定版本中都普遍存在,一直没有太大改善。虽然官方也在很早就承认这一问题,并且承诺尽快解决,但是至少到目前为止我们还没有看到哪一个版本较好的解决了这一问题。

  5. 尽量少 or
        当 where 子句中存在多个条件以“或”并存的时候,MySQL 的优化器并没有很好的解决其执行计划优化问题,再加上 MySQL 特有的 SQL 与 Storage 分层架构方式,造成了其性能比较低下,很多时候使用 union all 或者是union(必要的时候)的方式来代替“or”会得到更好的效果。

  6. 尽量用 union     all 代替 union
        union 和 union all 的差异主要是前者需要将两个(或者多个)结果集合并后再进行唯一性过滤操作,这就会涉及到排序,增加大量的 CPU 运算,加大资源消耗及延迟。所以当我们可以确认不可能出现重复结果集或者不在乎重复结果集的时候,尽量使用 union all 而不是 union。

  7. 尽量早过滤
        这一优化策略其实最常见于索引的优化设计中(将过滤性更好的字段放得更靠前)。
        在 SQL 编写中同样可以使用这一原则来优化一些     Join 的 SQL。比如我们在多个表进行分页数据查询的时候,我们最好是能够在一个表上先过滤好数据分好页,然后再用分好页的结果集与另外的表 Join,这样可以尽可能多的减少不必要的 IO 操作,大大节省 IO 操作所消耗的时间。

  8. 避免类型转换
        这里所说的“类型转换”是指 where 子句中出现     column 字段的类型和传入的参数类型不一致的时候发生的类型转换:

    • 人为在column_name      上通过转换函数进行转换
           直接导致 MySQL(实际上其他数据库也会有同样的问题)无法使用索引,如果非要转换,应该在传入的参数上进行转换

    • 由数据库自己进行转换
           如果我们传入的数据类型和字段类型不一致,同时我们又没有做任何类型转换处理,MySQL 可能会自己对我们的数据进行类型转换操作,也可能不进行处理而交由存储引擎去处理,这样一来,就会出现索引无法使用的情况而造成执行计划问题。

  9. 优先优化高并发的     SQL,而不是执行频率低某些“大”SQL
        对于破坏性来说,高并发的 SQL 总是会比低频率的来得大,因为高并发的 SQL 一旦出现问题,甚至不会给我们任何喘息的机会就会将系统压跨。而对于一些虽然需要消耗大量 IO 而且响应很慢的 SQL,由于频率低,即使遇到,最多就是让整个系统响应慢一点,但至少可能撑一会儿,让我们有缓冲的机会。

  10. 从全局出发优化,而不是片面调整
        SQL 优化不能是单独针对某一个进行,而应充分考虑系统中所有的 SQL,尤其是在通过调整索引优化 SQL 的执行计划的时候,千万不能顾此失彼,因小失大。

  11. 尽可能对每一条运行在数据库中的SQL进行 explain
        优化 SQL,需要做到心中有数,知道 SQL 的执行计划才能判断是否有优化余地,才能判断是否存在执行计划问题。在对数据库中运行的 SQL 进行了一段时间的优化之后,很明显的问题 SQL 可能已经很少了,大多都需要去发掘,这时候就需要进行大量的 explain 操作收集执行计划,并判断是否需要进行优化

 

 


Mysql性能优化之引擎的选择

 

MySQL 的存储引擎可能是所有关系型数据库产品中最具有特色的了,不仅可以同时使用多种存储引擎,而且每种存储引擎和MySQL之间使用插件方式这种非常松的耦合关系。

由于各存储引擎功能特性差异较大,这篇文章主要是介绍如何来选择合适的存储引擎来应对不同的业务场景。

  • MyISAM

    • 特性

      1. 不支持事务:MyISAM存储引擎不支持事务,所以对事务有要求的业务场景不能使用

      2. 表级锁定:其锁定机制是表级索引,这虽然可以让锁定的实现成本很小但是也同时大大降低了其并发性能

      3. 读写互相阻塞:不仅会在写入的时候阻塞读取,MyISAM还会在读取的时候阻塞写入,但读本身并不会阻塞另外的读

      4. 只会缓存索引:MyISAM可以通过key_buffer缓存以大大提高访问性能减少磁盘IO,但是这个缓存区只会缓存索引,而不会缓存数据

    • 适用场景

      1. 不需要事务支持(不支持)

      2. 并发相对较低(锁定机制问题)

      3. 数据修改相对较少(阻塞问题)

      4. 以读为主

      5. 数据一致性要求不是非常高

    • 最佳实践

      1. 尽量索引(缓存机制)

      2. 调整读写优先级,根据实际需求确保重要操作更优先

      3. 启用延迟插入改善大批量写入性能

      4. 尽量顺序操作让insert数据都写入到尾部,减少阻塞

      5. 分解大的操作,降低单个操作的阻塞时间

      6. 降低并发数,某些高并发场景通过应用来进行排队机制

      7. 对于相对静态的数据,充分利用Query       Cache可以极大的提高访问效率

      8. MyISAMCount只有在全表扫描的时候特别高效,带有其他条件的count都需要进行实际的数据访问

  • InnoDB

    • 特性

      1. 具有较好的事务支持:支持4个事务隔离级别,支持多版本读

      2. 行级锁定:通过索引实现,全表扫描仍然会是表锁,注意间隙锁的影响

      3. 读写阻塞与事务隔离级别相关

      4. 具有非常高效的缓存特性:能缓存索引,也能缓存数据

      5. 整个表和主键以Cluster方式存储,组成一颗平衡树

      6. 所有Secondary Index都会保存主键信息

    • 适用场景

      1. 需要事务支持(具有较好的事务特性)

      2. 行级锁定对高并发有很好的适应能力,但需要确保查询是通过索引完成

      3. 数据更新较为频繁的场景

      4. 数据一致性要求较高

      5. 硬件设备内存较大,可以利用InnoDB较好的缓存能力来提高内存利用率,尽可能减少磁盘       IO

    • 最佳实践

      1. 主键尽可能小,避免给Secondary       index带来过大的空间负担

      2. 避免全表扫描,因为会使用表锁

      3. 尽可能缓存所有的索引和数据,提高响应速度

      4. 在大批量小插入的时候,尽量自己控制事务而不要使用autocommit自动提交

      5. 合理设置innodb_flush_log_at_trx_commit参数值,不要过度追求安全性

      6. 避免主键更新,因为这会带来大量的数据移动

  • NDBCluster

    • 特性

      1. 分布式:分布式存储引擎,可以由多个NDBCluster存储引擎组成集群分别存放整体数据的一部分

      2. 支持事务:和Innodb一样,支持事务

      3. 可与mysqld不在一台主机:可以和mysqld分开存在于独立的主机上,然后通过网络和mysqld通信交互

      4. 内存需求量巨大:新版本索引以及被索引的数据必须存放在内存中,老版本所有数据和索引必须存在与内存中

    • 适用场景

      1. 具有非常高的并发需求

      2. 对单个请求的响应并不是非常的critical

      3. 查询简单,过滤条件较为固定,每次请求数据量较少,又不希望自己进行水平Sharding

    • 最佳实践

      1. 尽可能让查询简单,避免数据的跨节点传输

      2. 尽可能满足SQL节点的计算性能,大一点的集群SQL节点会明显多余Data节点

      3. 在各节点之间尽可能使用万兆网络环境互联,以减少数据在网络层传输过程中的延时

 

 

Mysql性能优化--- 包含SQL表结构、索引和缓存

 

  • 优化目标

    1. 减少 IO 次数
           IO
      永远是数据库最容易瓶颈的地方,这是由数据库的职责所决定的,大部分数据库操作中超过90%的时间都是      IO 操作所占用的,减少      IO 次数是      SQL 优化中需要第一优先考虑,当然,也是收效最明显的优化手段。

    2. 降低 CPU 计算
           
      除了      IO 瓶颈之外,SQL优化中需要考虑的就是      CPU 运算量的优化了。order      by, group by,distinct … 都是消耗 CPU 的大户(这些操作基本上都是      CPU 处理内存中的数据比较运算)。当我们的      IO 优化做到一定阶段之后,降低      CPU 计算也就成为了我们      SQL 优化的重要目标

  • 优化方法

    1. 改变 SQL 执行计划
           
      明确了优化目标之后,我们需要确定达到我们目标的方法。对于      SQL 语句来说,达到上述2个目标的方法其实只有一个,那就是改变      SQL 的执行计划,让他尽量少走弯路,尽量通过各种捷径来找到我们需要的数据,以达到      “减少      IO 次数”            “降低      CPU 计算”      的目标

  • 常见误区

    1. count(1)count(primary_key)      优于      count(*)
           
      很多人为了统计记录条数,就使用      count(1)      count(primary_key) 而不是 count(*) ,他们认为这样性能更好,其实这是一个误区。对于有些场景,这样做可能性能会更差,应为数据库对      count(*) 计数操作做了一些特别的优化。

    2. count(column)            count(*) 是一样的
           
      这个误区甚至在很多的资深工程师或者是      DBA 中都普遍存在,很多人都会认为这是理所当然的。实际上,count(column)            count(*) 是一个完全不一样的操作,所代表的意义也完全不一样。
           count(column)
      是表示结果集中有多少个column字段不为空的记录
           count(*)
      是表示整个结果集有多少条记录

    3. select      a,b from …      select a,b,c from … 可以让数据库访问更少的数据量
           
      这个误区主要存在于大量的开发人员中,主要原因是对数据库的存储原理不是太了解。
           
      实际上,大多数关系型数据库都是按照行(row)的方式存储,而数据存取操作都是以一个固定大小的IO单元(被称作      block 或者      page)为单位,一般为4KB8KB…      大多数时候,每个IO单元中存储了多行,每行都是存储了该行的所有字段(lob等特殊类型字段除外)。
           
      所以,我们是取一个字段还是多个字段,实际上数据库在表中需要访问的数据量其实是一样的。
           
      当然,也有例外情况,那就是我们的这个查询在索引中就可以完成,也就是说当只取      a,b两个字段的时候,不需要回表,而c这个字段不在使用的索引中,需要回表取得其数据。在这样的情况下,二者的IO量会有较大差异。

    4. order      by 一定需要排序操作
           
      我们知道索引数据实际上是有序的,如果我们的需要的数据和某个索引的顺序一致,而且我们的查询又通过这个索引来执行,那么数据库一般会省略排序操作,而直接将数据返回,因为数据库知道数据已经满足我们的排序需求了。
           
      实际上,利用索引来优化有排序需求的      SQL,是一个非常重要的优化手段
           
      延伸阅读:MySQL ORDER BY 的实现分析 MySQL      GROUP BY 基本实现原理 以及 MySQL DISTINCT 的基本实现原理 3篇文章中有更为深入的分析,尤其是第一篇

    5. 执行计划中有      filesort 就会进行磁盘文件排序
           
      有这个误区其实并不能怪我们,而是因为      MySQL 开发者在用词方面的问题。filesort      是我们在使用      explain 命令查看一条      SQL 的执行计划的时候可能会看到在      “Extra” 一列显示的信息。
           
      实际上,只要一条      SQL 语句需要进行排序操作,都会显示“Using      filesort”,这并不表示就会有文件排序操作。
           
      延伸阅读:理解 MySQL Explain 命令输出中的filesort,我在这里有更为详细的介绍

  • 基本原则

    1. 尽量少 join
           MySQL
      的优势在于简单,但这在某些方面其实也是其劣势。MySQL      优化器效率高,但是由于其统计信息的量有限,优化器工作过程出现偏差的可能性也就更多。对于复杂的多表      Join,一方面由于其优化器受限,再者在      Join 这方面所下的功夫还不够,所以性能表现离      Oracle 等关系型数据库前辈还是有一定距离。但如果是简单的单表查询,这一差距就会极小甚至在有些场景下要优于这些数据库前辈。

    2. 尽量少排序
           
      排序操作会消耗较多的      CPU 资源,所以减少排序可以在缓存命中率高等      IO 能力足够的场景下会较大影响      SQL 的响应时间。
           
      对于MySQL来说,减少排序有多种办法,比如:

      • 上面误区中提到的通过利用索引来排序的方式进行优化

      • 减少参与排序的记录条数

      • 非必要不对数据进行排序

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

    3. 尽量避免 select *
           
      很多人看到这一点后觉得比较难理解,上面不是在误区中刚刚说      select 子句中字段的多少并不会影响到读取的数据吗?
           
      是的,大多数时候并不会影响到      IO 量,但是当我们还存在      order by 操作的时候,select      子句中的字段多少会在很大程度上影响到我们的排序效率,这一点可以通过我之前一篇介绍 MySQL ORDER BY 的实现分析 的文章中有较为详细的介绍。
           
      此外,上面误区中不是也说了,只是大多数时候是不会影响到      IO 量,当我们的查询结果仅仅只需要在索引中就能找到的时候,还是会极大减少      IO 量的。

    4. 尽量用 join 代替子查询
           
      虽然      Join 性能并不佳,但是和      MySQL 的子查询比起来还是有非常大的性能优势。MySQL      的子查询执行计划一直存在较大的问题,虽然这个问题已经存在多年,但是到目前已经发布的所有稳定版本中都普遍存在,一直没有太大改善。虽然官方也在很早就承认这一问题,并且承诺尽快解决,但是至少到目前为止我们还没有看到哪一个版本较好的解决了这一问题。

    5. 尽量少 or
           
           where 子句中存在多个条件以并存的时候,MySQL      的优化器并没有很好的解决其执行计划优化问题,再加上      MySQL 特有的      SQL      Storage 分层架构方式,造成了其性能比较低下,很多时候使用      union all 或者是union(必要的时候)的方式来代替“or”会得到更好的效果。

    6. 尽量用 union all 代替      union
           union
           union all 的差异主要是前者需要将两个(或者多个)结果集合并后再进行唯一性过滤操作,这就会涉及到排序,增加大量的      CPU 运算,加大资源消耗及延迟。所以当我们可以确认不可能出现重复结果集或者不在乎重复结果集的时候,尽量使用      union all 而不是      union

    7. 尽量早过滤
           
      这一优化策略其实最常见于索引的优化设计中(将过滤性更好的字段放得更靠前)。
           
           SQL 编写中同样可以使用这一原则来优化一些      Join      SQL。比如我们在多个表进行分页数据查询的时候,我们最好是能够在一个表上先过滤好数据分好页,然后再用分好页的结果集与另外的表      Join,这样可以尽可能多的减少不必要的      IO 操作,大大节省      IO 操作所消耗的时间。

    8. 避免类型转换
           
      这里所说的类型转换是指      where 子句中出现      column 字段的类型和传入的参数类型不一致的时候发生的类型转换:

      • 人为在column_name 上通过转换函数进行转换
             
        直接导致       MySQL(实际上其他数据库也会有同样的问题)无法使用索引,如果非要转换,应该在传入的参数上进行转换

      •  

SELECT emp.ename, emp.job FROM emp WHERE emp.empno = 7369;
不要使用:SELECT emp.ename, emp.job FROM emp WHERE emp.empno = ‘7369
      • 由数据库自己进行转换
             
        如果我们传入的数据类型和字段类型不一致,同时我们又没有做任何类型转换处理,MySQL       可能会自己对我们的数据进行类型转换操作,也可能不进行处理而交由存储引擎去处理,这样一来,就会出现索引无法使用的情况而造成执行计划问题。

    1. 优先优化高并发的 SQL,而不是执行频率低某些”SQL
           
      对于破坏性来说,高并发的      SQL 总是会比低频率的来得大,因为高并发的      SQL 一旦出现问题,甚至不会给我们任何喘息的机会就会将系统压跨。而对于一些虽然需要消耗大量      IO 而且响应很慢的      SQL,由于频率低,即使遇到,最多就是让整个系统响应慢一点,但至少可能撑一会儿,让我们有缓冲的机会。

    2. 从全局出发优化,而不是片面调整
           SQL
      优化不能是单独针对某一个进行,而应充分考虑系统中所有的      SQL,尤其是在通过调整索引优化      SQL 的执行计划的时候,千万不能顾此失彼,因小失大。

    3. 尽可能对每一条运行在数据库中的SQL进行      explain
           
      优化      SQL,需要做到心中有数,知道      SQL 的执行计划才能判断是否有优化余地,才能判断是否存在执行计划问题。在对数据库中运行的      SQL 进行了一段时间的优化之后,很明显的问题      SQL 可能已经很少了,大多都需要去发掘,这时候就需要进行大量的      explain 操作收集执行计划,并判断是否需要进行优化。



二、MySQL 数据库性能优化之表结构

很多人都将 数据库设计范式 作为数据库表结构设计圣经,认为只要按照这个范式需求设计,就能让设计出来的表结构足够优化,既能保证性能优异同时还能满足扩展性要求。殊不知,在N年前被奉为圣经的数据库设计3范式早就已经不完全适用了。这里我整理了一些比较常见的数据库表结构设计方面的优化技巧,希望对大家有用。由于MySQL数据库是基于行(Row)存储的数据库,而数据库操作 IO 的时候是以 pageblock)的方式,也就是说,如果我们每条记录所占用的空间量减小,就会使每个page中可存放的数据行数增大,那么每次 IO 可访问的行数也就增多了。反过来说,处理相同行数的数据,需要访问的 page 就会减少,也就是 IO 操作次数降低,直接提升性能。此外,由于我们的内存是有限的,增加每个page中存放的数据行数,就等于增加每个内存块的缓存数据量,同时还会提升内存换中数据命中的几率,也就是缓存命中率。

  • 数据类型选择
       
    数据库操作中最为耗时的操作就是     IO 处理,大部分数据库操作     90% 以上的时间都花在了     IO 读写上面。所以尽可能减少     IO 读写量,可以在很大程度上提高数据库操作的性能。我们无法改变数据库中需要存储的数据,但是我们可以在这些数据的存储方式方面花一些心思。下面的这些关于字段类型的优化建议主要适用于记录条数较多,数据量较大的场景,因为精细化的数据类型设置可能带来维护成本的提高,过度优化也可能会带来其他的问题:

    1. 数字类型:非万不得已不要使用DOUBLE,不仅仅只是存储长度的问题,同时还会存在精确性的问题。同样,固定精度的小数,也不建议使用DECIMAL,建议乘以固定倍数转换成整数存储,可以大大节省存储空间,且不会带来任何附加维护成本。对于整数的存储,在数据量较大的情况下,建议区分开      TINYINT / INT / BIGINT 的选择,因为三者所占用的存储空间也有很大的差别,能确定不会使用负数的字段,建议添加unsigned定义。当然,如果数据量较小的数据库,也可以不用严格区分三个整数类型。

    2. 字符类型:非万不得已不要使用 TEXT 数据类型,其处理方式决定了他的性能要低于char或者是varchar类型的处理。定长字段,建议使用      CHAR 类型,不定长字段尽量使用      VARCHAR,且仅仅设定适当的最大长度,而不是非常随意的给一个很大的最大长度限定,因为不同的长度范围,MySQL也会有不一样的存储处理。

    3. 时间类型:尽量使用TIMESTAMP类型,因为其存储空间只需要      DATETIME 类型的一半。对于只需要精确到某一天的数据类型,建议使用DATE类型,因为他的存储空间只需要3个字节,比TIMESTAMP还少。不建议通过INT类型类存储一个unix      timestamp 的值,因为这太不直观,会给维护带来不必要的麻烦,同时还不会带来任何好处。

    4. ENUM      & SET:对于状态字段,可以尝试使用      ENUM 来存放,因为可以极大的降低存储空间,而且即使需要增加新的类型,只要增加于末尾,修改结构也不需要重建表数据。如果是存放可预先定义的属性数据呢?可以尝试使用SET类型,即使存在多种属性,同样可以游刃有余,同时还可以节省不小的存储空间。

    5. LOB类型:强烈反对在数据库中存放      LOB 类型数据,虽然数据库提供了这样的功能,但这不是他所擅长的,我们更应该让合适的工具做他擅长的事情,才能将其发挥到极致。在数据库中存储      LOB 数据就像让一个多年前在学校学过一点Java的营销专业人员来写      Java 代码一样。

  • 字符编码
       
    字符集直接决定了数据在MySQL中的存储编码方式,由于同样的内容使用不同字符集表示所占用的空间大小会有较大的差异,所以通过使用合适的字符集,可以帮助我们尽可能减少数据量,进而减少IO操作次数。

    1. 纯拉丁字符能表示的内容,没必要选择      latin1 之外的其他字符编码,因为这会节省大量的存储空间

    2. 如果我们可以确定不需要存放多种语言,就没必要非得使用UTF8或者其他UNICODE字符类型,这回造成大量的存储空间浪费

    3. MySQL的数据类型可以精确到字段,所以当我们需要大型数据库中存放多字节数据的时候,可以通过对不同表不同字段使用不同的数据类型来较大程度减小数据存储量,进而降低      IO 操作次数并提高缓存命中率

  • 适当拆分
       
    有些时候,我们可能会希望将一个完整的对象对应于一张数据库表,这对于应用程序开发来说是很有好的,但是有些时候可能会在性能上带来较大的问题。当我们的表中存在类似于     TEXT 或者是很大的     VARCHAR类型的大字段的时候,如果我们大部分访问这张表的时候都不需要这个字段,我们就该义无反顾的将其拆分到另外的独立表中,以减少常用数据所占用的存储空间。这样做的一个明显好处就是每个数据块中可以存储的数据条数可以大大增加,既减少物理     IO 次数,也能大大提高内存中的缓存命中率。

上面几点的优化都是为了减少每条记录的存储空间大小,让每个数据库中能够存储更多的记录条数,以达到减少 IO 操作次数,提高缓存命中率。下面这个优化建议可能很多开发人员都会觉得不太理解,因为这是典型的反范式设计,而且也和上面的几点优化建议的目标相违背。

  • 适度冗余
       
    为什么我们要冗余?这不是增加了每条数据的大小,减少了每个数据块可存放记录条数吗?确实,这样做是会增大每条记录的大小,降低每条记录中可存放数据的条数,但是在有些场景下我们仍然还是不得不这样做:

    1. 被频繁引用且只能通过 Join      2张(或者更多)大表的方式才能得到的独立小字段
           
      这样的场景由于每次Join仅仅只是为了取得某个小字段的值,Join到的记录又大,会造成大量不必要的      IO,完全可以通过空间换取时间的方式来优化。不过,冗余的同时需要确保数据的一致性不会遭到破坏,确保更新的同时冗余字段也被更新

  • 尽量使用 NOT NULL
        NULL
    类型比较特殊,SQL     难优化。虽然     MySQL NULL类型和     Oracle NULL     有差异,会进入索引中,但如果是一个组合索引,那么这个NULL     类型的字段会极大影响整个索引的效率。此外,NULL     在索引中的处理也是特殊的,也会占用额外的存放空间。
       
    很多人觉得     NULL 会节省一些空间,所以尽量让NULL来达到节省IO的目的,但是大部分时候这会适得其反,虽然空间上可能确实有一定节省,倒是带来了很多其他的优化问题,不但没有将IO量省下来,反而加大了SQLIO量。所以尽量确保     DEFAULT 值不是     NULL,也是一个很好的表结构设计优化习惯。

 三、MySQL 数据库性能优化之索引优化

大家都知道索引对于数据访问的性能有非常关键的作用,都知道索引可以提高数据访问效率。为什么索引能提高数据访问性能?他会不会有副作用?是不是索引创建越多,性能就越好?到底该如何设计索引,才能最大限度的发挥其效能?这篇文章主要是带着上面这几个问题来做一个简要的分析,同时排除了业务场景所带来的特殊性,请不要纠结业务场景的影响。

  • 索引为什么能提高数据访问性能?
       
    很多人只知道索引能够提高数据库的性能,但并不是特别了解其原理,其实我们可以用一个生活中的示例来理解。我们让一位不太懂计算机的朋友去图书馆确认一本叫做《MySQL性能调优与架构设计》的书是否在藏,这样对他说:请帮我借一本计算机类的数据库书籍,是属于     MySQL 数据库范畴的,叫做《MySQL性能调优与架构设计》。朋友会根据所属类别,前往存放计算机书籍区域的书架,然后再寻找数据库类存放位置,再找到一堆讲述“MySQL”的书籍,最后可能发现目标在藏(也可能已经借出不在书架上)。在这个过程中:     “计算机”->“数据库”->“MySQL”->“在藏”->MySQL性能调优与架构设计》其实就是一个根据索引查找数据的典型案例,计算机”->“数据库”->“MySQL”->“在藏”     就是朋友查找书籍的索引。假设没有这个索引,那查找这本书的过程会变成怎样呢?朋友只能从图书馆入口一个书架一个书架的遍历,直到找到《MySQL性能调优与架构设计》这本书为止。如果幸运,可能在第一个书架就找到。但如果不幸呢,那就惨了,可能要将整个图书馆所有的书架都找一遍才能找到我们想要的这本书。注:这个例子中的索引是记录在朋友大脑中的,实际上,每个图书馆都会有一个非常全的实际存在的索引系统(大多位于入口显眼处),由很多个贴上了明显标签的小抽屉构成。这个索引系统中存放这非常齐全详尽的索引数据,标识出我们需要查找的目标在某个区域的某个书架上。而且每当有新的书籍入库,旧的书籍销毁以及书记信息修改,都需要对索引系统进行及时的修正。

下面我们通过上面这个生活中的小示例,来分析一下索引,看看能的出哪些结论?

  • 索引有哪些副作用

    1. 图书的变更(增,删,改)都需要修订索引,索引存在额外的维护成本

    2. 查找翻阅索引系统需要消耗时间,索引存在额外的访问成本

    3. 这个索引系统需要一个地方来存放,索引存在额外的空间成本

  • 索引是不是越多越好?

    1. 如果我们的这个图书馆只是一个进出中转站,里面的新书进来后很快就会转发去其他图书馆而从这个馆藏中清除,那我们的索引就只会不断的修改,而很少会被用来查找图书
           
      所以,对于类似于这样的存在非常大更新量的数据,索引的维护成本会非常高,如果其检索需求很少,而且对检索效率并没有非常高的要求的时候,我们并不建议创建索引,或者是尽量减少索引。

    2. 如果我们的书籍量少到只有几本或者就只有一个书架,索引并不会带来什么作用,甚至可能还会浪费一些查找索引所花费的时间。
           
      所以,对于数据量极小到通过索引检索还不如直接遍历来得快的数据,也并不适合使用索引。

    3. 如果我们的图书馆只有一个10平方的面积,现在连放书架都已经非常拥挤,而且馆藏还在不断增加,我们还能考虑创建索引吗?
           
      所以,当我们连存储基础数据的空间都捉襟见肘的时候,我们也应该尽量减少低效或者是去除索引。

  • 索引该如何设计才高效?

    1. 如果我们仅仅只是这样告诉对方的:帮我确认一本数据库类别的讲述      MySQL 的叫做《MySQL性能调优与架构设计》的书是否在藏,结果又会如何呢?朋友只能一个大类区域一个大类区域的去寻找数据库类别,然后再找到      “MySQL”范畴,再看到我们所需是否在藏。由于我们少说了一个计算机类,朋友就必须到每一个大类去寻找。
           
      所以,我们应该尽量让查找条件尽可能多的在索引中,尽可能通过索引完成所有过滤,回表只是取出额外的数据字段。

    2. 如果我们是这样说的:帮我确认一本讲述      MySQL 的数据库范畴的计算机丛书,叫做《MySQL性能调优与架构设计》,看是否在藏。如果这位朋友并不知道计算机是一个大类,也不知道数据库属于计算机大类,那这位朋友就悲剧了。首先他得遍历每个类别确认“MySQL”存在于哪些类别中,然后从包含      “MySQL” 书籍中再看有哪些是数据库范畴的(有可能部分是讲述PHP或者其他开发语言的),然后再排除非计算机类的(虽然可能并没有必要),然后才能确认。
           
      所以,字段的顺序对组合索引效率有至关重要的作用,过滤效果越好的字段需要更靠前。

    3. 如果我们还有这样一个需求(虽然基本不可能):帮我将图书馆中所有的计算机图书借来。朋友如果通过索引来找,每次都到索引柜找到计算机书籍所在的区域,然后从书架上搬下一格(假设只能以一格为单位从书架上取下,类比数据库中以block/page为单位读取),取出第一本,然后再从索引柜找到计算机图书所在区域,再搬下一格,取出一本…      如此往复直至取完所有的书。如果他不通过索引来找又会怎样呢?他需要从地一个书架一直往后找,当找到计算机的书,搬下一格,取出所有计算机的书,再往后,直至所有书架全部看一遍。在这个过程中,如果计算机类书籍较多,通过索引来取所花费的时间很可能要大于直接遍历,因为不断往复的索引翻阅所消耗的时间会非常长。(延伸阅读:这里有一篇以前写的关于Oracle的文章,索引扫描还是全表扫描(Index Scan Or Full Table Scan
           
      所以,当我们需要读取的数据量占整个数据量的比例较大抑或者说索引的过滤效果并不是太好的时候,使用索引并不一定优于全表扫描。

    4. 如果我们的朋友不知道数据库这个类别可以属于计算机这个大类,抑或者图书馆的索引系统中这两个类别属性并没有关联关系,又会怎样呢?也就是说,朋友得到的是2个独立的索引,一个是告知计算机这个大类所在的区域,一个是数据库这个小类所在的区域(很可能是多个区域),那么他只能二者选其一来搜索我的需求。即使朋友可以分别通过2个索引检索然后自己在脑中取交集再找,那这样的效率实际过程中也会比较低下。
           
      所以,在实际使用过程中,一次数据访问一般只能利用到1个索引,这一点在索引创建过程中一定要注意,不是说一条SQL语句中Where子句里面每个条件都有索引能对应上就可以了。

    5. 最后总结一下法则:不要在建立的索引的数据列上进行下列操作:

避免对索引字段进行计算操作避免在索引字段上使用not,,!=避免在索引列上使用IS NULLISNOT NULL避免在索引列上出现数据类型转换避免在索引字段上使用函数避免建立索引的列中使用空值。

 四、MySQL 数据库性能优化之缓存参数优化

数据库属于 IO 密集型的应用程序,其主要职责就是数据的管理及存储工作。而我们知道,从内存中读取一个数据库的时间是微秒级别,而从一块普通硬盘上读取一个IO是在毫秒级别,二者相差3个数量级。所以,要优化数据库,首先第一步需要优化的就是IO,尽可能将磁盘IO转化为内存IO。本文先从 MySQL 数据库IO相关参数(缓存参数)的角度来看看可以通过哪些参数进行IO优化:

  • query_cache_size/query_cache_type     global     Query cache 作用于整个     MySQL Instance,主要用来缓存     MySQL 中的     ResultSet,也就是一条SQL语句执行的结果集,所以仅仅只能针对select语句。当我们打开了     Query Cache 功能,MySQL在接受到一条select语句的请求后,如果该语句满足Query     Cache的要求(未显式说明不允许使用Query     Cache,或者已经显式申明需要使用Query     Cache),MySQL     会直接根据预先设定好的HASH算法将接受到的select语句以字符串方式进行hash,然后到Query     Cache 中直接查找是否已经缓存。也就是说,如果已经在缓存中,该select请求就会直接将数据返回,从而省略了后面所有的步骤(如     SQL语句的解析,优化器优化以及向存储引擎请求数据等),极大的提高性能。当然,Query     Cache 也有一个致命的缺陷,那就是当某个表的数据有任何任何变化,都会导致所有引用了该表的select语句在Query     Cache 中的缓存数据失效。所以,当我们的数据变化非常频繁的情况下,使用Query     Cache 可能会得不偿失。Query     Cache的使用需要多个参数配合,其中最为关键的是     query_cache_size     query_cache_type ,前者设置用于缓存     ResultSet 的内存大小,后者设置在何场景下使用     Query Cache。在以往的经验来看,如果不是用来缓存基本不变的数据的MySQL数据库,query_cache_size     一般     256MB 是一个比较合适的大小。当然,这可以通过计算Query     Cache的命中率(Qcache_hits/(Qcache_hits+Qcache_inserts)*100))来进行调整。query_cache_type可以设置为0(OFF)1(ON)或者2(DEMOND),分别表示完全不使用query     cache,除显式要求不使用query     cache(使用sql_no_cache)之外的所有的select都使用query     cache,只有显示要求才使用query     cache(使用sql_cache)。

  • binlog_cache_size     global     Binlog Cache 用于在打开了二进制日志(binlog)记录功能的环境,是     MySQL 用来提高binlog的记录效率而设计的一个用于短时间内临时缓存binlog数据的内存区域。一般来说,如果我们的数据库中没有什么大事务,写入也不是特别频繁,2MB4MB是一个合适的选择。但是如果我们的数据库大事务较多,写入量比较大,可与适当调高binlog_cache_size。同时,我们可以通过binlog_cache_use     以及     binlog_cache_disk_use来分析设置的binlog_cache_size是否足够,是否有大量的binlog_cache由于内存大小不够而使用临时文件(binlog_cache_disk_use)来缓存了。

  • key_buffer_size     global     Key Buffer 可能是大家最为熟悉的一个     MySQL 缓存参数了,尤其是在     MySQL 没有更换默认存储引擎的时候,很多朋友可能会发现,默认的     MySQL 配置文件中设置最大的一个内存参数就是这个参数了。key_buffer_size     参数用来设置用于缓存     MyISAM存储引擎中索引文件的内存区域大小。如果我们有足够的内存,这个缓存区域最好是能够存放下我们所有的     MyISAM 引擎表的所有索引,以尽可能提高性能。此外,当我们在使用MyISAM     存储的时候有一个及其重要的点需要注意,由于     MyISAM 引擎的特性限制了他仅仅只会缓存索引块到内存中,而不会缓存表数据库块。所以,我们的     SQL 一定要尽可能让过滤条件都在索引中,以便让缓存帮助我们提高查询效率。

  • bulk_insert_buffer_size     thread)和key_buffer_size一样,这个参数同样也仅作用于使用     MyISAM存储引擎,用来缓存批量插入数据的时候临时缓存写入数据。当我们使用如下几种数据写入语句的时候,会使用这个内存区域来缓存批量结构的数据以帮助批量写入数据文件:insert     … select …
        insert … values (…) ,(…),(…)…
        load data infile… into… (
    非空表)

  • innodb_buffer_pool_sizeglobal)当我们使用InnoDB存储引擎的时候,innodb_buffer_pool_size     参数可能是影响我们性能的最为关键的一个参数了,他用来设置用于缓存     InnoDB 索引及数据块的内存区域大小,类似于     MyISAM 存储引擎的     key_buffer_size 参数,当然,可能更像是     Oracle     db_cache_size。简单来说,当我们操作一个     InnoDB 表的时候,返回的所有数据或者去数据过程中用到的任何一个索引块,都会在这个内存区域中走一遭。和key_buffer_size     对于     MyISAM 引擎一样,innodb_buffer_pool_size     设置了     InnoDB 存储引擎需求最大的一块内存区域的大小,直接关系到     InnoDB存储引擎的性能,所以如果我们有足够的内存,尽可将该参数设置到足够打,将尽可能多的     InnoDB 的索引及数据都放入到该缓存区域中,直至全部。我们可以通过     (Innodb_buffer_pool_read_requests – Innodb_buffer_pool_reads) /     Innodb_buffer_pool_read_requests * 100% 计算缓存命中率,并根据命中率来调整     innodb_buffer_pool_size 参数大小进行优化。

  • innodb_additional_mem_pool_sizeglobal)这个参数我们平时调整的可能不是太多,很多人都使用了默认值,可能很多人都不是太熟悉这个参数的作用。innodb_additional_mem_pool_size     设置了InnoDB存储引擎用来存放数据字典信息以及一些内部数据结构的内存空间大小,所以当我们一个MySQL     Instance中的数据库对象非常多的时候,是需要适当调整该参数的大小以确保所有数据都能存放在内存中提高访问效率的。这个参数大小是否足够还是比较容易知道的,因为当过小的时候,MySQL     会记录     Warning 信息到数据库的     error log 中,这时候你就知道该调整这个参数大小了。

  • innodb_log_buffer_size     global)这是     InnoDB 存储引擎的事务日志所使用的缓冲区。类似于     Binlog BufferInnoDB     在写事务日志的时候,为了提高性能,也是先将信息写入     Innofb Log Buffer 中,当满足 innodb_flush_log_trx_commit 参数所设置的相应条件(或者日志缓冲区写满)之后,才会将日志写到文件(或者同步到磁盘)中。可以通过     innodb_log_buffer_size 参数设置其可以使用的最大内存空间。
       
    注:innodb_flush_log_trx_commit     参数对     InnoDB Log 的写入性能有非常关键的影响。该参数可以设置为012,解释如下:0log     buffer中的数据将以每秒一次的频率写入到log     file中,且同时会进行文件系统到磁盘的同步操作,但是每个事务的commit并不会触发任何log     buffer log     file的刷新或者文件系统到磁盘的刷新操作;
        1
    :在每次事务提交的时候将log     buffer 中的数据都会写入到log     file,同时也会触发文件系统到磁盘的同步;
        2
    :事务提交会触发log     buffer log     file的刷新,但并不会触发磁盘文件系统到磁盘的同步。此外,每秒会有一次文件系统到磁盘同步操作。此外,MySQL文档中还提到,这几种设置中的每秒同步一次的机制,可能并不会完全确保非常准确的每秒就一定会发生同步,还取决于进程调度的问题。实际上,InnoDB     能否真正满足此参数所设置值代表的意义正常     Recovery 还是受到了不同     OS 下文件系统以及磁盘本身的限制,可能有些时候在并没有真正完成磁盘同步的情况下也会告诉     mysqld 已经完成了磁盘同步。

  • innodb_max_dirty_pages_pct     global)这个参数和上面的各个参数不同,他不是用来设置用于缓存某种数据的内存大小的一个参数,而是用来控制在     InnoDB Buffer Pool 中可以不用写入数据文件中的Dirty     Page 的比例(已经被修但还没有从内存中写入到数据文件的脏数据)。这个比例值越大,从内存到磁盘的写入操作就会相对减少,所以能够一定程度下减少写入操作的磁盘IO。但是,如果这个比例值过大,当数据库     Crash 之后重启的时间可能就会很长,因为会有大量的事务数据需要从日志文件恢复出来写入数据文件中。同时,过大的比例值同时可能也会造成在达到比例设定上限后的     flush 操作过猛而导致性能波动很大。

上面这几个参数是 MySQL 中为了减少磁盘物理IO而设计的主要参数,对 MySQL 的性能起到了至关重要的作用。

—EOF—

按照 mcsrainbow 朋友的要求,这里列一下根据以往经验得到的相关参数的建议值:

  • query_cache_type     : 如果全部使用innodb存储引擎,建议为0,如果使用MyISAM     存储引擎,建议为2,同时在SQL语句中显式控制是否是哟你gquery     cache

  • query_cache_size:     根据命中率(Qcache_hits/(Qcache_hits+Qcache_inserts)*100))进行调整,一般不建议太大,256MB可能已经差不多了,大型的配置型静态数据可适当调大

  • binlog_cache_size:     一般环境2MB4MB是一个合适的选择,事务较大且写入频繁的数据库环境可以适当调大,但不建议超过32MB

  • key_buffer_size:     如果不使用MyISAM存储引擎,16MB足以,用来缓存一些系统表信息等。如果使用     MyISAM存储引擎,在内存允许的情况下,尽可能将所有索引放入内存,简单来说就是越大越好

  • bulk_insert_buffer_size:     如果经常性的需要使用批量插入的特殊语句(上面有说明)来插入数据,可以适当调大该参数至16MB32MB,不建议继续增大,某人8MB

  • innodb_buffer_pool_size:     如果不使用InnoDB存储引擎,可以不用调整这个参数,如果需要使用,在内存允许的情况下,尽可能将所有的InnoDB数据文件存放如内存中,同样将但来说也是越大越好

  • innodb_additional_mem_pool_size:     一般的数据库建议调整到8MB16MB,如果表特别多,可以调整到32MB,可以根据error     log中的信息判断是否需要增大

  • innodb_log_buffer_size:     默认是1MB,系的如频繁的系统可适当增大至4MB8MB。当然如上面介绍所说,这个参数实际上还和另外的flush参数相关。一般来说不建议超过32MB

  • innodb_max_dirty_pages_pct:     根据以往的经验,重启恢复的数据如果要超过1GB的话,启动速度会比较慢,几乎难以接受,所以建议不大于     1GB/innodb_buffer_pool_size(GB)*100 这个值。当然,如果你能够忍受启动时间比较长,而且希望尽量减少内存至磁盘的flush,可以将这个值调整到90,但不建议超过90

注:以上取值范围仅仅只是我的根据以往遇到的数据库场景所得到的一些优化经验值,并不一定适用于所有场景,所以在实际优化过程中还需要大家自己不断的调整分析,也欢迎大家随时通过 Mail 与我联系沟通交流优化或者是架构方面的技术,一起探讨相互学习。

 

 

Mysql优化总结

 

一、索引
1
、创建索引:
1).ALTERTABLE   
 ALTER TABLE
用来创建普通索引、UNIQUE索引或PRIMARYKEY索引。    
    
 ALTER TABLE table_name ADD INDEX index_name(column_list)   
  
 ALTER TABLE table_name ADD UNIQUE (column_list)   
  
 ALTER TABLE table_name ADD PRIMARY KEY (column_list)   
  
2)、CREATEINDEX   
 CREATE INDEX
可对表增加普通索引或UNIQUE索引。   
  
 CREATE INDEX index_name ON table_name (column_list)   
  
 CREATE UNIQUE INDEX index_name ON table_name (column_list)  
2
、查看索引  
  
 mysql> show index from tblname;   
  
 mysql> show keys from tblname; 
3
、删除索引
 
可利用ALTERTABLEDROPINDEX语句来删除索引。类似于CREATEINDEX语句,DROPINDEX可以在ALTERTABLE 内部作为一条语句处理,语法如下。  
 DROP INDEX index_name ON talbe_name   
  
 ALTER TABLE table_name DROP INDEX index_name   
  
 ALTER TABLE table_name DROP PRIMARY KEY   

索引:http://www.cnblogs.com/hustcat/archive/2009/10/28/1591648.html
**explain +select ·····
用来获取select语句的执行的相关信息及索引的使用等
**describe table table_name;
**analyze table table_name;
查看表的信息,帮助优化
**show
查看执行状态

二、my.ini中的配置
http://www.chinaz.com/program/2009/1210/100740.shtml
mysql > show status;
可以查看具体的设置服务器的状态
具体的配置呀什么,没有亲自试验过

三、数据表引擎
 1
MyISAMmysql默认的
 2
InnoDB:支持事务、锁、外键、聚簇索引
引擎介绍:http://blog.csdn.net/cheungjustin/article/details/5999880
 http://limaolinjia.blog.163.com/blog/static/539162282011012145139/

四、索引的类型:
 1
B-Tree索引
 2
hash索引
具体的参考还是一)

五、事务
数据表引擎使用InnoDB
http://www.cnblogs.com/winner/archive/2011/11/09/2242272.html


六、存储过程
经编译和优化后存储在数据库服务器中,运行效率高,可以降低客户机和服务器之间的通信量,有利于集中控制,易于维护P247
http://blog.sina.com.cn/s/blog_52d20fbf0100ofd5.html

七、mysqlprofilingmysql性能分析器)优化sql语句
查看SQL执行消耗系统资源的信息
++++
需要开启+++
具体使用:http://www.jiunile.com/mysql-profiling%E7%9A%84%E4%BD%BF%E7%94%A8.html

八、慢查询日志
++++
需要开启++++
通过慢日志查询可以知道哪些SQL语句执行效率低下,那些sql语句使用的频率高等
MySQL查询语句的监控、分析、优化是MySQL优化非常重要的一步。开启慢查询日志后,由于日志记录操作,在一定程度上会占用CPU资源影响mysql的性能,但是可以阶段性开启来定位性能瓶颈。
具体参考:http://blog.csdn.net/renzhenhuai/article/details/8839874

关于mysql的一些讲解:http://www.ccvita.com/category/mysql


版权声明:本站内容全部来自于腾讯微信公众号,属第三方自助推荐收录。《第十四更——数据库性能优化详解》的版权归原作者「LionHeart心有猛虎细嗅蔷薇」所有,文章言论观点不代表Lambda在线的观点, Lambda在线不承担任何法律责任。如需删除可联系QQ:516101458

文章来源: 阅读原文

相关阅读

关注LionHeart心有猛虎细嗅蔷薇微信公众号

LionHeart心有猛虎细嗅蔷薇微信公众号:gh_5713a987ecb4

LionHeart心有猛虎细嗅蔷薇

手机扫描上方二维码即可关注LionHeart心有猛虎细嗅蔷薇微信公众号

LionHeart心有猛虎细嗅蔷薇最新文章

精品公众号随机推荐