MySQL性能优化浅析

news2025/1/17 2:56:27

1. 硬件

1.1 CPU

IO密集型,提升CPU核心数

计算密集型,提升CPU频率

1.2 磁盘

机械硬盘在随机访问时,由于受磁针移动速度的限制,性能会大幅降低。使用固态硬盘可以大幅提升随机访问的能力。按需选择。

1.3 其他

带宽、内存频率

2. 数据库设置

2.1 InnoDB Buffer Pool

Buffer Pool是内存中的一块区域,用于缓存索引、行数据、自适应hash索引、Change Buffer、锁信息等,同时,InnoDB也通过Buffer Pool来实现延迟写,这样就能通过批量写入来提升性能。可以说,Buffer Pool的运行状况会直接影响到InnoDB的性能表现。

理论上来说,Buffer Pool越大,运行时性能越好,当所有数据都在Buffer Pool中时性能达到最优。但是,这显然是不行的。原因如下:

  1. 数据的访问并不是均匀的,所以内存跟性能并不是一个线性的关系。一个缓存了热点数据的Buffer Pool跟一个缓存了所有数据的Buffer Pool,它们在性能上的表现差异并不大。最后,Buffer Pool本身也有最大值限制,在32位跟64位系统分别为: 2 32 − 1 2^{32} -1 2321 2 64 − 1 2^{64}-1 2641
  2. 磁盘中的数据往往是TB甚至PB级的,内存的价格远高于磁盘
  3. 更大的Buffer Pool会产生更多的脏页,shut down的速度会严重受影响

所以,Buffer Pool的大小应该通过测量实际的工作负载(最常访问的数据的总大小)得出。

Buffer Pool由Buffer Pool Instance组成。instance的数量跟大小分别由innodb_buffer_pool_instances、innodb_buffer_pool_chunk_size
来控制。Buffer Pool的总大小等于innodb_buffer_pool_instances * innodb_buffer_pool_chunk_size或者直接指定innodb_buffer_pool_size,单位是Byte。

另外Buffer Pool 支持自适应大小,通过设置innodb_dedicated_server为enable来开启,它会根据服务器的内存自动设置Buffer Pool的大小

Server MemoryBuffer Pool Size
Less than 1GB128MB (the default value)
1GB to 4GBserver memory * 0.5
Greater than 4GBserver memory * 0.75

注意,官方建议只有在MySQL的专用服务器上才适合开启innodb_dedicated_server

默认情况下,MySQL在shut down时会将Buffer Pool中的数据dump一个快照保存到磁盘中,当再次启动时,又会将数据从磁盘中加载回来。这种预热的方式可以立即让Buffer Pool回到shut down之前的状态,避免了因重启而导致热数据需要从磁盘中读的问题。该行为通过innodb_buffer_pool_dump_at_shutdown 、innodb_buffer_pool_load_at_startup、innodb_buffer_pool_dump_pct控制。

Buffer Pool的实现算法是LRU的一个变种,通过一个Mid Point将LRU列表分成了young跟old两个部分,young最近最多访问的数据,old存新加载到Buffer Pool的以及最近最少访问的数据。

如果系统中

Log Buffer

在事物并发比较高时,可以通过增加Log Buffer的大小来使每次的Flush操作尽可能包含更多已提交的事物。

Log Buffer的大小通过innodb_log_buffer_size控制,默认为16MB。这个值也应该根据每次可以Flush的最大数据量来设置,太大没有意义。

另外,也可以通过牺牲一点持久性来换取性能。将innodb_flush_log_at_trx_commit设置为2或者0。0拥有更好的性能,相应的丢数据的风险也更大。具体区别可参考MySQL之InnoDB架构浅析

《高性能MySQL》里的推荐做法是将innodb_flush_log_at_trx_commit设置为1,并将日志文件放在一个有电池支持的写缓存和SSD的RAID卷上,写缓存用于提高写入的性能,电池则为数据提供crash-safe的能力,SSD提高随机访问的能力。

2.2 MVCC

REPEATABLE READ隔离级别下,长事物的更新会导致更多的查询事物需要通过undo log来查看旧版本的数据,所以,减小事物的粒度(范围,时间)、将更新操作后移可以有效的减少这种情况。

2.3自增模式

在MySQL8之前的版本可以通过将innodb_autoinc_lock_mode设置为2,前提是MySQL的主从复制模式是基于Row的。否则可能会导致自增id不一致。

2.4 IO

如果硬盘的实际性能比较好,可以相应的增大innodb_io_capacity的值,这可以提升Buffer Pool后台进程flush的性能。该参数默认为200

2.5 BinLog

据说阿里的BinLog In Redo有用,暂时没仔细研究

3. 设计

3.1 表设计

尽量避免NULL列

对MySQL来说,引用可空列的查询更难优化,因为它们使索引、索引统计和值比较更加复杂。可空列使用更多的存储空间,并需要在MySQL内部进行特殊处理。

尽量使用较小或者简单的数据类型

较小的数据类型通常会更快,因为它们在磁盘、内存和CPU缓存中使用的空间更少。它们通常也需要更少的CPU周期来处理。另外,整数要比字符串的比较成本更低,因为字符集和排序规则使字符的比较变得很复杂

冗余&汇总

对于部分经常需要聚合函数进行统计的表,可以通过创建汇总表来降低查询成本。

对于高频的多表查询或者表连接,可以考虑冗余部分字段

选择合适的数据类型

  1. 避免使用字符串作为主键,尤其是UUID、MD5这类函数生成的值。原因如下:

    • 字符串相对数值来说,需要更多的存储空间,且处理起来更复杂
    • UUID产生的值具备随机性,这会导致页分裂、随机磁盘访问、聚集索引碎片,同时还会减慢SELECT查询的速度,因为逻辑上相邻的行会在磁盘内存中不一定是相邻的,最后,这种随机性也破坏了局部性原理,这可能会使热点数据很分散进而导致热点数据被频繁的刷出跟miss
  2. 对于固定的几种数据值,比如性别、状态等,可以考虑使用ENUM类型存储。ENUM会将字符串存储为数值,并保留数值跟字符串的映射关系。这能很有效的节省存储空间。

  3. varcharchar

    varchar需要用额外的空间来存储数据的长度,同时,对varchar列数据的更新操作可能会产生页分裂的情况,这会产生额外的性能消耗。所以:

    • 在数据的最大长度跟平均长度相差比较大时可以考虑使用varchar,这样可以节省存储空间。另外,需要注意的是,不要为了一时方便将varchar的大小设置得过大,一定要与实际相符,因为在临时表以及排序中,varchar是按照设置的最大长度来分配内存的
    • 数据的长度很小或者基本相等的情况下,使用char。char不需要额外的字节来存储数据的长度
  4. DATETIMETIMESTAMP

    • TIMESTAMP:4字节存储空间,支持时区,允许自动更新,取值范围是1970-2038。
    • DATETIME :8字节存储空间,取值范围1000-9999

    TIMESTAMP具备更好的性能以及功能性,但它也存在只能存储到2038年的问题。在选择时,可以从以下几个点思考:

    • 需要向后支持多远的时间?
    • 日期、时间和时区处理转移到MySQL,还是在代码中处理?
    • 存储空间对数据存储有多大影响?

    正常情况下,像跟当前时间相关的列比如说数据的创建&更新时间,TIMESTAMP是不二之选。一方面是因为2038到现在还比较遥远(很多小公司能不能活到那个时候都是个问题…),眼下的性能以及功能性的提升相对于未来可能发生的迁移在很多场景是都是高性价比的。另一方面,个人觉得MySQL团队在未来会针对TIMESTAMP的范围局限性作出相应更新。

  5. 存正数可以在类型前加上UNSIGNED,这可以使正数的范围增加一倍

索引&SQL

索引是存储引擎用来快速查找行的数据结构,也是提高查询性能的最有力的方法。同时,索引的好坏对性能的影响也是非常巨大的。

在InnoDB中,索引按B+树构造(如图),分为聚集索引跟二级索引。聚集索引的叶子节点中存储了具体的行数据,二级索引的叶子节点中则存储了主键的值。对于通过二级索引查找完整行数据时,需要先通过二级索引找到主键,再通过主键定位到行数据,这个过程也称为回表。

在这里插入图片描述

由于B+树是按序构建的,这种有序性一方面避免了低效的随机IO访问(相同的值或者相近的值会被放到一起),另一方面对于Order By 这类语句也有着很好的加速作用。

在查找的时候,索引按照从左到右的顺序进行数据匹配(最左前缀),这适用于索引的内容顺序以及索引的顺序(多列索引)。

索引的性能还与索引值的重复度(区分度)息息相关,重复度越高,性能越低。

接下来看看索引相关的优化操作

  • count统计行数避免传入大列,多索引列

    总行数统计,索引数据越小,统计速度越快。除非需要统计某列不为空的行数。否则应该使用count(*)这类不指定列名的方式来统计,此时MySQL优化器会自动选择最小的那个索引进行扫描,如果是带条件的,count(*)也会自动选择合适的索引来扫描。

    可通过explain的key_len查看,值越小,性能越高

  • 设置主键

    设置主键或者至少一个唯一索引(在没有主动设置主键的情况下,MySQL会使用该索引作为主键/聚集索引),否则MySQL会为表生成一个隐式主键。这会导致两个问题:

    1. 无法享受主键带来的高性能(无需回表)
    2. 隐式主键的值分配是引擎层面的,所有表共享,存在互斥量的竞争
  • 覆盖索引

    InnoDB在获取数据时,如果二级索引里包含了所需的数据,就不会再回表去获取完整的行数据。这个过程也称为Index condition pushdown

  • 最左前缀匹配

    • 按索引顺序组织where子句中的查询条件,避免出现”跳列“的情况,比如按A、B、C三列组织索引,查询条件为A、C。则C不会影响索引的遍历路线。但是Index condition pushdown在这里依然是生效的。
    • 模糊匹配不应为’前’模糊。虽然’前’模糊也能走索引扫描来定位行数据或者享受覆盖索引带来的性能优势,但是无法利用索引的顺序性,简而言之就是得扫描整棵索引树才能完成结果集的查找(explain里的type为index)。
  • 避免在组合索引的非最右索引中使用范围查找

    对于多列索引,比如以A、B、C三列建立索引,如果对A或者B进行模糊匹配或者其他范围查找,这时候,对C的搜索无法应用索引。这就意味着存储引擎会把所有只满足A、B的行数据返回给server,由server来做进一步的筛选。

  • 选择合适的索引长度

    在为BLOB或TEXT列或很长的VARCHAR列建立索引时,必须定义前缀索引,因为MySQL无法为其全长建立索引。
    列的前缀通常是有选择性的,这种选择性指的是列数据之间数据的重复度,重复度越低,选择性越好,高选择性意味着索引的高性能。

    在空间与选择性之间衡量,选择一个足够长的前缀,既能提供良好的选择性,又足够短以节省空间。当选择的前缀长度使得所有前缀都互不相同时,这时从功能上说,在前缀上建立索引跟在整列建立索引是等效的。更小的空间消耗意味着更多的索引值以及更少的IO次数,所以大部分时候(查多列数据时),前缀索引的性能要更优。

  • 选择合适的索引顺序

    将重复度更低的列放在组合索引的更左边,这能更高效的过滤掉不符合条件的数据。

  • 尽量保证索引值有序插入

    有序的插入可以尽量避免页分裂,页分裂需要移动数据,同时回造成更多的空间浪费以及随机磁盘访问(数据不集中,change buffer刷盘效率大幅降低),尤其是对聚集索引来说(需要移动更多的数据)。

  • 避免滥用索引

    索引列的数据发生变化时,不仅要维护行数据,也要维护索引数据。索引会拖慢CUD的速度。对于某些可能没有使用到的索引,可以通过如下语句查找:

    SELECT * FROM sys.schema_unused_indexes;
    
  • 运行ANALYZE TABLE

    由于InnoDB只会在表第一次打开、表大小发生显著变化、以及执行ANALYZE TABLE时才会更新表的统计信息,所以,有时候可以手动执行ANALYZE TABLE来帮助优化器能做出更优选择。

  • 运行OPTIMIZE TABLE或者ALTER TABLE <table> ENGINE=InnoDB;减少磁盘碎片
    数据不紧凑时,索引的访问、全表扫描都会变慢,因为要加载很多无用的数据(空间)到内存。

  • update或者delete语句的where子句里的条件对应的列最好加上索引,尤其是在Repeatable Read隔离级别下,全表扫描会锁住所有扫描过的记录,直到更新提交或回滚

SQL&锁&应用层

  • 只查询确实需要的数据

    • SELECT * 是否值得,否则应将*换成需要的列名
    • 是否有重复的执行
  • 避免长事物

    长事物会加剧锁的竞争,在Repeatable Read隔离级别下,还会导致MVCC的成本增加。避免长事物的方案有:

    • 拆分事物,减小事物的粒度。包括
      • 将影响范围大的Update SQL拆分成影响范围小的SQL
      • 影响范围大的Update条件拆分成影响范围小的条件分批执行,比如通过limit来限制影响的行数
      • 控制业务代码的事物粒度(比如在Spring构建的应用里,将标注Transactional的方法拆分或者使用编程式事物来缩小事物的粒度)
    • 将update操作延后,将耗时更长的操作延后
  • 避免重复查询

    比如,同样或者类似的SQL,参数不同。这会增加MySQL的解析成本以及IO成本(数据在磁盘时)。所以,合并这类查询,将数据拿到应用层来过滤也是一种提升性能的方法

  • or 改为 in

    in 在MySQL里会先排序再二分查找,时间复杂度是 O ( log ⁡ n ) O(\log n) O(logn),n是参数的数量

    or 会对条件一个一个查找,时间复杂度是 O ( n ) O(n) O(n)

  • join

    • 非驱动表的on 或者 using指定的列建立索引
    • 行数更少的表作为驱动表,MySQL大多数情况下会自动做出最优选择。前提是连接的表数量不超过optimizer_search_depth指定的值,该值默认是62,过大的值会导致MySQL在join的表数量很大时会在优化上消耗大量时间。可以通过将optimizer_search_depth设置为0来让MySQL自动确定是否进行join的全部可能性的搜索优化或者使用贪心算法来优化。
    • 避免order by 或者group by 里的列跨表
  • 避免深分页

    像select * from <table> limit m, n这样的操作,MySql会取出m+n行记录,然后丢弃掉前m行。所以,当m很大的时候,性能就会非常低。

    可以先通过索引找到第m行到第m+n行的id,然后根据id取出后面的n行即可。

    select * from <table> INNER JOIN (
        SELECT id FROM <table>
        ORDER BY <index column> LIMIT m, n
        ) AS tmp USING(id);
    

    或者事先知道第m行的id,且排序也是按主键的顺序,则SQL可以直接写为:

    select * from <table> where id>=<id from m.th>,n
    
  • union vs union all

    union会过滤重复数据,如果业务上不会有重复数据的情况,则使用union all可以节省判断重复数据时产生的性能损耗

  • 避免在where子句中的索引列上做函数运算或者类型转换,比如concat、cast、或者算数运算等

    这会导致索引失效

3.2 数据分片

  • 将行数很大的表按行将数据拆分到小表中,可以降低B树的高度提升索引的效率,另外,如果将表拆分到不同的数据库实例中,IO跟CPU性能也能得到提升
  • 将表里一些不常用的大字段拆分到其他表中,可以增加聚集索引页字节点的数据紧凑度,从而更少的IO能获取更多的数据。

4. 指标

MySQL通过存储引擎提供的索引的页数、表和索引的重复度、行和索引键的长度,以及键分布信息来生成执行计划。通过explain可以看到相关的统计信息。相关指标参考**8.8.2 EXPLAIN Output Format**

5. 参考

《High Performance MySQL》 4th edition

MySQL Official Documentation

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/93696.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Superset 安装配置

文章目录Superset 安装配置一、Superset 概述1. Superset简介2. 功能概述3. 支持的数据库二、Superset 环境部署步骤三、创建虚拟机&#xff0c;安装CentOS1.下载CentOS2.创建虚拟机3.编辑虚拟机设置4.安装centos7.9mini版本5.启动centos&#xff0c;并进行登录四、CentOS配置1…

小米(Android)刷NetHunter安装指南

一、安装NetHunter 前提&#xff1a;确保手机已经root&#xff0c;已装上magisk。如果没有root&#xff0c;可用尝试magisk root 后执行此文 1、下载Nethunter&#xff1a;Get Kali | Kali Linux 然后push 到sdcard 里&#xff0c; 2、打开magisk&#xff0c;选择刚刚下好的…

Windows下安装libtorch与Clion配置

Windows 安装和使用libtorch 1.下载libtorch libtorch的下载链接&#xff0c;如下图所示&#xff0c;libtorch有release和debug版本可以选择。为了方便调试&#xff0c;下debug版。电脑上没CUDA&#xff0c;下次有需要再更新吧。 2.libtorch使用 在Visual Studio的使用可以参…

云原生周刊 | 让 ChatGPT 以电子邮件的方式来解释 KubeSphere

过去的一周是 ChatGPT 的狂欢&#xff0c;我猜每一位云原生玩家都很好奇他是如何看待 Kubernetes 的。咱们不防换个方式来提问&#xff0c;让它使用电子邮件的方式来向别人推荐 KubeSphere 和 OpenFunction。 开源项目推荐 Tailscale Ingress Controller 这是针对 Tailscale …

【DevOps实战系列】第七章:详解Docker私服Harbor篇

个人亲自录制全套DevOps系列实战教程 &#xff1a;手把手教你玩转DevOps全栈技术 Harbor私服搭建 讲完Nexus3再来看下harbor&#xff0c;其实大同小异&#xff0c;只不过harbor的管理要比Nexus3更专业、功能更完善&#xff0c;大家按需选择即可&#xff0c;Nexus的优势是他能和…

web网站工程项目前期需求分析与规划怎么写?

在当下&#xff0c;判断一份网站工程项目文档是否优秀&#xff0c;项目目录是最直接的体现&#xff0c;同时&#xff0c;工程说明、需求分析和项目规划各版块的内容都缺一不可。工欲善其事必先利其器&#xff0c;前期准备得越充分&#xff0c;后期就会越顺利。 本期&#xff0c…

Centos7安装图形化界面并使用Windows远程桌面连接(包含离线部署)

一、在centos7 中部署远程桌面所使用的程序 1、关闭防火墙和selinux(xrdp是通过3389端口远程桌面连接 ) [rootlocalhost ~]# systemctl stop firewalld #临时关闭防火墙 [rootlocalhost ~]# systemctl disable firewalld.service #永久关闭防火墙 [rootlocalhost ~]# setenf…

SpringMVC:SpringMVC之JSON数据传输参数(5)

JSON数据传输参数1 JSON数据传输参数2 JSON普通数组3 JSON对象数据4 JSON对象数组5 小结1 JSON数据传输参数 现在比较流行的开发方式为异步调用。前后台以异步方式进行交换&#xff0c;传输的数据使用的是JSON,所以前端如果发送的是JSON数据&#xff0c;后端该如何接收? 对于…

面试官:单体架构怎么向分布式微服务架构演变的?(8000字干货)

随着网站规模越来越大&#xff0c;单体应用往往很难再满足要求&#xff0c;就需要向分布式&#xff0c;微服务架构演变。 那么这个演变过程是怎么样的呢&#xff1f;都涉及到哪些组件&#xff0c;会遇到哪些问题&#xff0c;以及相应的解决方案都是什么&#xff0c;本篇文章就…

ESP 低功耗入门

此篇博客以 ESP32 为例来说明 ESP 的睡眠模式。 ESP32 芯片可以大致分成以为五个模块&#xff1a; RF 模块&#xff0c;也就是射频模块&#xff0c;用于蓝牙 / Wi-Fi 的收发(TX / RX)功能。CPU&#xff0c;如 Xtensa 内核 (ESP32 / ESP32-S2 / ESP32-S3)&#xff0c;RISC-V 内…

ssm项目-商城管理系统

1 逆向工程设计 1.1 xml文件配置&#xff08;generatorConfig.xml&#xff09; <?xml version"1.0" encoding"UTF-8"?> <!DOCTYPE generatorConfigurationPUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"&q…

滑动窗口的最大值【滑动窗口问题】

文章目录题目解题思路代码展示题目 给定一个长度为 n 的数组 num 和滑动窗口的大小 size &#xff0c;窗口从最左边滑动到最右边&#xff0c;每次向右边滑动一个位置&#xff0c;找出所有滑动窗口里数值的最大值。 窗口大于数组长度或窗口长度为0的时候&#xff0c;返回空 数…

硬件需知知识 -- 基本元件(电阻)

一、电阻 1.1 贴片电阻 1.1.1 贴片电阻的封装大小是和功率时相关的。 封装大小功率(W)0201120\frac{1}{20}201​0402116\frac{1}{16}161​0603110\frac{1}{10}101​080518\frac{1}{8}81​12060.2518120.5或1201012\frac{1}{2}21​25121或者21.1.2 贴片电阻读数 贴片电阻的读数…

Spring Security 竟然可以同时存在多个过滤器链?

目录Spring Security 中的过滤器多个过滤器链多个过滤器链配置例子http.authorizeRequests() 开头是什么意思&#xff1f;引用Spring Security 中的过滤器 Spring Security 中认证、授权功能的实现机制是通过过滤器来实现的。Spring Security 中一共提供了 32 个过滤器&#x…

Nacos集群版本安装(三)

一、前言 注&#xff1a;我这里的nacos集群安装环境使用的是本地MacOs系统&#xff0c;使用的是同一个IP不同端口&#xff0c;这里就不再讲解具体的目录结构之内的了&#xff0c;下面我们开始演示具体的配置和安装。了解nacos单机版本安装 二、nacos集群安装部署&#xff1a; …

Redis常见面试题(二)

目录 1、Redis和Memcached有什么区别? 2、Redis支持哪些数据类型? 3、Redis支持JSON数据类型吗?为什么? 4、Redis模块系统有什么用? 5、Redis支持对象映射模型吗? 6、Redis默认支持多少个数据库?怎么修改? 7、Redis SET命令可以代替SETNX吗? 8、Redis单个实例最…

【浅学Java】索引的分类、创建、删除以及新特性

索引的创建和设计原则1. 索引的分类1.1 普通索引1.2 唯一性索引1.3 主键索引1.4 单列索引1.5 多列&#xff08;联合&#xff0c;组合&#xff09;索引1.6 全文索引2. 索引的创建2.1 创建表时创建索引1. 隐式创建2. 显式创建3. 全文检索2.2 创建表后创建索引1. alter table 的方…

基于java(ssm)家教管理平台(java毕业设计)

基于java&#xff08;ssm&#xff09;家教管理平台 家教管理&#xff0c;是基于java变成语言&#xff0c;mysql数据库&#xff0c;ssm框架和idea工具开发&#xff0c;本系统分为用户&#xff0c;管理员&#xff0c;教师三个角色&#xff0c;其中用户可以注册&#xff0c;登陆&…

网络工程师之海明校验

海明校验&#xff08;又称汉明码&#xff09; 基本思想 将有效信息按某种规律分成若干组&#xff0c;每组安排一个校验位&#xff0c;做奇偶测试&#xff0c;就能提供多位检错信息&#xff0c;以指出最大可能是哪位出错&#xff0c;从而将其纠正。 特点 它不仅具有检测错误的…

深入浅出网络编程TCP,UDP,Socket,Http网络编程面试题

目录 什么是网络编程 网络编程中的主要问题 计算机网络体系结构 TCP / UDP Socket HTTP 什么是网络编程 网络编程中的主要问题 计算机网络体系结构 OSI参考模型 OSI&#xff08;Open System Interconnect&#xff09;&#xff0c;即开放式系统互联。一般都叫OSI参考模型&…