暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

传统关系型数据库性能优化全攻略

Alleria Windrunner 2020-02-29
1003
篇我们来说一说传统关系型数据库性能优化攻略。

影响性能的因素

一般来说影响数据库性能的因素主要可以分为两类:

  • 业务需求
  • 系统架构

业务需求
为什么要把业务需求放在这里说呢?因为有很多时候不合理的业务需求才是影响性能的重大杀手。比如:
  • 实时count(*)

  • 深翻页

  • 无用功能堆积

  • 不断需求升级造成系统复杂,影响整体性能

  • 过度重视用户体验


以上这些其实我们从业务上就可以规避掉的性能问题直接从业务上规避掉就好了。

系统架构

至于系统架构可以从以下几方面考虑:

  • 数据库中存放的数据是否都适合
  • 大表是否拆分
  • 是否合理的利用了Cache机制
  • 数据层的实现是否都是最精简
  • 超复杂的sql语句
  • 过度依赖数据库Query语句功能导致效率低下
  • 对扩展性过度追求,导致将对象拆的过于离散
  • 重复执行相同的Query语句

先说数据库存放的数据是否合适,比如二进制音视频等富媒体数据、流水队列数据、超大文本数据。然后是对于大表是否有进行水平拆分。是否有合理的利用Cache机制,比如有没有将访问频繁且更新较少的数据放入Cache,具体来说有系统配置及规则数据、活跃用户的基本信息、活跃用户的个性化定制信息、准实时的统计信息。最后是数据层的实现是否都是最精简,举个具体的例子来说:

    SELECT id,subject,url FROM photo WHERE user_id = 1 LIMIT 10
    然后查询详细信息的时候,有的同学就会用上面查出的id运行10次下面的sql:
      SELECT COUNT(*) FROM photo_comment WHERE photo_id = ?
      其实我们可以通过把查询出的id拼接到一起然后执行下列sql,减少sql执行的网络开销:
        SELECT photo_id, count(*) FROM photo_comment WHERE photo_id IN(?) GROUP BY photo_id
        以上我们从业务需求和系统架构层面分析了影响性能的因素。接下来我们来看看在数据库设计阶段怎么进行优化。

        数据库设计阶段优化

        我们常说好的开始能决定好的结束,所以对于数据库性能这块来说,一个好的设计往往很大程度上能影响咱们数据库的性能。具体来说,我们可以从以下这些点着手:

        • 适度冗余-尽量减少join
        • 大字段垂直拆分
        • 大表水平拆分
        • 选用合适的数据类型

        对于互联网应用来说我们应当尽量减少连表查询,适当的冗余。对于大字段,比如blob字段我们可以将其拆分存储在key-value存储之中,然后映射id字段到原表。


        Query优化
        说完了数据库设计阶段,我们再来聊一聊最主要的部分Query优化。
        • 优化更需要优化的Query

        • 定位优化对象的性能瓶颈

        • 明确优化目标

        • 从Explain入手

        • 优化的基本原则


        首先我们应该明确哪些Query更需要优化,比如有个sql执行时间为1s,但是一天只执行一次,而另外一个sql执行时间为0.5s,但是一天执行1w次,所以我们要确定哪些Query更需要优化,优化哪些Query能给系统整体带来更大收益,高并发低消耗和低并发高消耗那个更需要我们去优化?其次我们要找到瓶颈在哪里?是CPU还是IO?多使用Explain执行计划分析sql语句,定位问题。最后有一些优化的基本原则:
        • 多使用Profile

        • 永远用小结果集驱动大结果集,而非小表驱动大表

        • 尽可能在索引中完成排序

        • 只取自己需要的列

        • 仅仅使用最有效的过滤条件

        • 尽可能避免复杂的Join和子查询

        • 为了性能或功能上的需要,可以使用MySQL的HINT,如果用FORCE INDEX强制使用某个索引

        • 大量的排序操作影响性能,尽量减少ORDER BY,GROUP BY操作,可以使用程序替代

        • 对于经常更新的字符串使用char而不要使用varchar

        • 对容易产生碎片的表使用OPTIMIZE TABLE


        索引设计优化

        关于索引优化这块我们可以从以下三点入手:

        • 索引规范
        • 方法技巧
        • 注意事项

        先来说索引规范:

        • 适合添加索引的列应尽量满足如下条件:
          • 出现在WHERE或ON中的列
          • 具有较少的重复值
          • 以查询操作为主,对于更新操作远远大于查询操作的列,不要创建索引(考虑索引维护的代价)

        • 尽量使用短索引,短索引更容易被MySQL加载到内存中,从而达到提高读性能的目的,这点对主键尤其重要

        • 更新频繁的表,数据量较小时尽量不要建立索引,过多的索引会导致插入、更新、删除,甚至于查询性能的下降

        • 一般情况下不要使用FORCE INDEX强制使用某个索引,尽量让MySQL优化器决定使用哪个索引

        • 建议尽量减少对主键索引的更新,这样会导致所有辅助索引的更新

        • 建议做性能测试的时候根据需要禁止adaptive hash index


        然后是一些基于经验的方法技巧:

        • 较频繁的作为查询条件的字段可考虑创建索引
        • 为写多读少的表创建索引时,请充分考虑索引对写性能的影响
        • SQL中尽量在索引里完成排序
        • 对于B-Tree复合索引(多维索引),只有WHERE条件中包含最左前缀(复合索引中的第一列)时,MySQL才会利用该复合索引(除非索引列全部为数值类型时)

        最后是一些注意事项:

        • 唯一性太差的字段不适合单独创立索引,即使是频繁的作为查询条件
        • 更新非常频繁的字段不适合建立索引
        • 不出现在WHERE子句中的字段不该建立索引
        • WHERE子句中尽量不要使用LIKE '%Content%',以通配符开头的字符串不会使用索引
        • WHERE子句中需要使索引列独立。MySQL不会利用计算后的索引列(除非该索引列为数值类型的主索引)

        锁优化
                MySQL中的所可以分为:
        • 行级锁

        • 表级锁

        • 页面锁


        行级锁开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。表级锁开销小,加锁快;不会出现死锁;锁定力度大,发生锁冲突的概率最高,并发度最低。而对于页面锁,开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。对于锁优化的一些经验技巧可以从以下这些点入手:
        • InnoDB支持行级锁,要合理利用InnoDB的行级锁

        • 合理设计索引,让InnoDB在索引键上面加锁的时候尽可能准确,尽可能的缩小锁定范围,避免造成不必要的锁定而影响其他Query的执行

        • 尽可能让所有的数据检索都通过索引来完成,从而避免InnoDB因为无法通过索引加锁而升级为表级锁

        • 尽可能减少基于范围的数据检索过滤条件,避免为间隙锁带来的负面影响而锁定了不该锁定的记录

        • 尽量控制事务的大小,减少锁定的资源量和锁定时间长度


        范式和冗余
        这其实是一个古老的话题,但是我们也来看一下:
        • 范式与冗余没有绝对的对与错,取决于具体的设计需求和性能需求;

        • 读多写少性能要求高的应用

          • 可以采取加入冗余数据、牺牲范式、提升性能

          • 可以放弃约束的使用,例如外键约束,提升性能

          • 可以牺牲范式,减小记录数规模,如违背1NF,不遵守属性不可再分

          • 负面影响:应用代码的复杂性增加,更新操作代码增大

        总之一句话,对于互联网应用,一般都是范式让步于冗余。
        文章转载自Alleria Windrunner,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

        评论