MySQL优化

mysql性能调优的思路

  1. 最好的优化—–不查询! 这不是开玩笑.
  2. 了解性能浪费比例: 业务逻辑过多的查询>表结构不合理>sql语句效率低>硬件
  3. 如果一台服务器出现长时间负载过高 /周期性负载过大,或偶尔卡住
    如何来处理?
    答: 大的思路:
    是周期性的变化还是偶尔问题?
    是服务器整体性能的问题, 还是某单条语句的问题?
    具体到单条语句, 这条语句是在等待上花的时间,还是查询上花的时间.
    唯一的办法—–监测并观察服务器的状态.
    1:观察服务器状态, 一般用如下2个命令
    Show status; Show processlist;
    例: mysql> show status;
    #mysqladmin ext

MySQL周期性波动解决办法:

  1. 减少无关请求(业务逻辑层面,暂不讨论,但其实是最有效的手段)
  2. 如果请求数是一定的,不可减少的. 我们要尽量让请求数平稳,不要有剧烈波动.
    很多时候,不是服务器撑不住总的查询量,而是在某个时间段撑不住高峰请求.
  3. 夜间负载低时,集中失效. 短时间内会有波峰,但夜间访问量少,因此波峰并不剧烈,当到上午10点左右人多时,缓存已经建立了一部分. 白天时,波峰也不剧烈.
  4. 或者让缓存的生命周期在一定范围内随机,也可以减缓波峰剧烈的情况

对于不规则的延迟现象的观察

不规则的延迟现象往往是由于效率低下的语句造成的,如何抓到这些效率低的语句.
可以用show processlist命令长期观察,或用慢查询.

如果观察到以下状态,则需要注意
converting HEAP to MyISAM
查询结果太大时,把结果放在磁盘 (语句写的不好,取数据太多)

create tmp table
创建临时表(如group时储存中间结果,说明索引建的不好)

Copying to tmp table on disk
把内存临时表复制到磁盘 (索引不好,表字段选的不好)

locked
被其他查询锁住 (一般在使用事务时易发生,互联网应用不常发生)

logging slow query
记录慢查询

临时表在如下几种情况被创建:

  • 如果group by 的列没有索引,必产生内部临时表,
  • 如果order by 与group by为不同列时,或多表联查时order by ,group by 包含的列不是第一张表的列,将会产生临时表(实验1)
  • distinct 与order by 一起使用可能会产生临时表(实验2)
  • union合并查询时会用到临时表

想确定查询是否需要临时表,可以用EXPLAIN查询计划,并查看Extra列,看是否有Using temporary.

通过前面实验,可以看出,数据库的优化是一个系统工程:

  1. 建表: 表结构的拆分,如核心字段都用int,char,enum等定长结构
    非核心字段,或用到text,超长的varchar,拆出来单放一张表.

  2. 建索引: 合理的索引可以减少内部临时表(索引优化策略里详解)

  3. 写语句: 不合理的语句将导致大量数据传输以及内部临时表的使用.

表的优化与列类型选择

表的优化:

  1. 定长与变长分离
    如 id int, 占4个字节, char(4) 占4个字符长度,也是定长, time
    即每一单元值占的字节是固定的.
    核心且常用字段,宜建成定长,放在一张表.
    而varchar, text,blob,这种变长字段,适合单放一张表, 用主键与核心表关联起来.
  2. 常用字段和不常用字段要分离.
    需要结合网站具体的业务来分析,分析字段的查询场景,查询频度低的字段,单拆出来.
  3. 合理添加冗余字段.

列选择原则:

  1. 字段类型优先级 整型 > date,time > enum,char>varchar > blob
  2. 够用就行,不要慷慨 (如smallint,varchar(N))
  3. 尽量避免用NULL()

索引优化策略

1:索引类型
1.1 B-tree索引
注: 名叫btree索引,大的方面看,都用的平衡树,但具体的实现上, 各引擎稍有不同,
比如,严格的说,NDB引擎,使用的是T-tree
Myisam,innodb中,默认用B-tree索引

但抽象一下—B-tree系统,可理解为”排好序的快速查找结构”.

1.2 hash索引
在memory表里,默认是hash索引, hash的理论查询时间复杂度为O(1)

疑问: 既然hash的查找如此高效,为什么不都用hash索引?
答:
1. hash函数计算后的结果,是随机的,如果是在磁盘上放置数据,
比主键为id为例, 那么随着id的增长, id对应的行,在磁盘上随机放置.
2. 不法对范围查询进行优化.
3. 无法利用前缀索引. 比如 在btree中, field列的值“hellopworld”,并加索引
查询 xx=helloword,自然可以利用索引, xx=hello,也可以利用索引. (左前缀索引)
因为hash(‘helloword’),和hash(‘hello’),两者的关系仍为随机
4. 排序也无法优化.
5. 必须回行.就是说 通过索引拿到数据位置,必须回到表中取数据

2: btree索引的常见误区
2.1 在where条件常用的列上都加上索引
例: where cat_id=3 and price>100 ; //查询第3个栏目,100元以上的商品
误: cat_id上,和, price上都加上索引.
错: 只能用上cat_id或Price索引,因为是独立的索引,同时只能用上1个.

2.2 在多列上建立索引后,查询哪个列,索引都将发挥作用
误: 多列索引上,索引发挥作用,需要满足左前缀要求.

聚簇索引与非聚簇索引

innodb和myisam的索引区别:

innodb的主索引文件上 直接存放该行数据,称为聚簇索引,次索引指向对主键的引用
myisam中, 主索引和次索引,都指向物理行(磁盘位置).

聚簇索引的优劣势

优势: 根据主键查询条目比较少时,不用回行(数据就在主键节点下)
劣势: 如果碰到不规则数据插入时,造成频繁的页分裂.

高性能索引策略

对于innodb而言,因为节点下有数据文件,因此节点的分裂将会比较慢.
对于innodb的主键,尽量用整型,而且是递增的整型.
如果是无规律的数据,将会产生的页的分裂,影响速度.

索引覆盖:

索引覆盖是指 如果查询的列恰好是索引的一部分,那么查询只需要在索引文件上进行,不需要回行到磁盘再找数据.
这种查询速度非常快,称为”索引覆盖”

理想的索引

  1. 查询频繁
  2. 区分度高
  3. 长度小
  4. 尽量能覆盖常用查询字段.

延迟关联

select a.* from it_area as a inner join (select id from it_area where name like '%东山%') as t on a.id=t.id;

第2种做法中, 内层查询,只沿着name索引层顺序走, name索引层包含了id值的.
所以,走完索引层之后,找到所有合适的id,
再通过join, 用id一次性查出所有列. 走完name列再取.

第1种做法: 沿着name的索引文件走, 走到满足的条件的索引,就取出其id,
并通过id去取数据, 边走边取.

通过id查找行的过程被延后了. — 这种技巧,称为”延迟关联”.

索引与排序

排序可能发生2种情况:
1: 对于覆盖索引,直接在索引上查询时,就是有顺序的, using index
2: 先取出数据,形成临时表做filesort(文件排序,但文件可能在磁盘上,也可能在内存中)

我们的争取目标—–取出来的数据本身就是有序的! 利用索引来排序.

sql语句优化

  1. sql语句的时间花在哪儿?
    答: 等待时间 , 执行时间.
    这两个时间并非孤立的, 如果单条语句执行的快了,对其他语句的锁定的也就少了.
    所以,我们来分析如何降低执行时间.

  2. sql语句的执行时间,又花在哪儿了?
    答:
    a: 查 —-> 沿着索引查,甚至全表扫描
    b: 取 —-> 查到行后,把数据取出来(sending data)

  3. sql语句的优化思路?

    • 不查, 通过业务逻辑来计算, 比如论坛的注册会员数,我们可以根据前3个月统计的每天注册数, 用程序来估算.

    • 少查, 尽量精准数据,少取行. 我们观察新闻网站,评论内容等,一般一次性取列表 10-30条左右.

    • 必须要查,尽量走在索引上查询行.

in 型子查询引出的陷阱

原因: mysql的查询优化器,针对In型做优化,被改成了exists的执行效果.
当goods表越大时, 查询速度越慢.

改进: 用连接查询来代替子查询

explain select goods_id,g.cat_id,g.goods_name from  goods as g inner join (select cat_id from ecs_category where parent_id=6) as t using(cat_id) \G

from 型子查询:

注意::内层from语句查到的临时表, 是没有索引的.
所以: from的返回内容要尽量少.

count() 优化

误区:
1. myisam的count()非常快
答: 是比较快,.但仅限于查询表的”所有行”比较快, 因为Myisam对行数进行了存储.
一旦有条件的查询, 速度就不再快了.尤其是where条件的列上没有索引.

  1. 假如,id<100的商家都是我们内部测试的,我们想查查真实的商家有多少?
select count(*) from lx_com where id>=100;  (1000多万行用了6.X秒)

小技巧:

select count(*) from lx_com; 快
select count(*) from lx_com where id<100; 快
select count(*) frol lx_com -select count(*) from lx_com where id<100; 快
select (select count(*) from lx_com) - (select count(*) from lx_com where id<100)

group by

注意:
1. 分组用于统计,而不用于筛选数据.
比如: 统计平均分,最高分,适合, 但用于筛选重复数据,则不适合.
以及用索引来避免临时表和文件排序
2. 以A,B表连接为例 ,主要查询A表的列,
那么 group by ,order by 的列尽量相同,而且列应该显示声明为A的列
3. union优化
注意: union all 不过滤 效率提高,如非必须,请用union all
因为 union去重的代价非常高, 放在程序里去重.

limit 及翻页优化

limit offset,N, 当offset非常大时, 效率极低,
原因是mysql并不是跳过offset行,然后单取N行,而是取offset+N行,返回放弃前offset行,返回N行.效率较低,当offset越大时,效率越低

优化办法:
1. 从业务上去解决
办法: 不允许翻过100页
以百度为例,一般翻页到70页左右.
2. 不用offset,用条件查询.

为什么用单表查询

  1. 单表查询的缓存命中比多表连接查询高
  2. 为了方便以后的分表分库
  3. 单表查询速度比多表连接查询快

猜你喜欢

转载自blog.csdn.net/github_38383183/article/details/78937695
今日推荐