存储引擎

查看命令

  1. show engines;
  2. show variables like '%storage_engine%'

Inno DB和MyISAM对比

Mysql5.5 版本之前默认的存储引擎就是 MyISAM 存储引擎,MySQL 中比较多的系统表使用 MyISAM 存储引擎,系统临时表也会用到 MyISAM 存储引擎,但是在 Mysql5.5 之后默认的存储引擎就是 InnoDB 存储引擎了。原因主要是 MyISAM 是表级锁定,限制了数据库读/写的性能;另外一个原因 MyISAM 不支持事务,基于以上两点,InnoDB 引擎使用的非常广泛。

对比 InnoDB MyISAM
文件存储方式 .frm 表定义文件;.ibd 数据文件 .frm 表定义文件;.myd 数据文件;.myi 索引文件
索引方式 B+ Tree B+ Tree
count(*) 操作 全表扫描 无需扫描
锁机制 表锁、 行锁 表锁
事务 支撑事务的 ACID 不支持事务
常用场景 读写操作 读多写少操作,比如配置表

常见存储引擎介绍

Inno DB

MySQL的默认事务型引擎、它被用来处理大量短期事务。 除非有非常特别的原因需要使用其他的存储引擎,否则建议优先考虑Inno DB引擎。

MyISAM

提供了大量的特性,包含全文索引,压缩,空间行数等, 但不支持事务和行级锁,有个缺陷就是崩溃后无法恢复

Memory

Memory存储引擎使用存在内存中的内容来创建表。每个 Memory表实际只对应一个磁盘文件,格式是 .frm。Memory类型的表访问速度很快,因为其数据是存放在内存中。默认使用 HASH 索引。

Merge

Merge存储引擎是一组 MyISAM 表的组合,Merge表本身没有数据,对 Merge类型的表进行查询、更新、删除的操作, 实际上是对内部的 MyISAM 表进行的。Merge表在磁盘上保留两个文件,一个是.frm文件存储表定义、 一个是 .MRG文件存储 Merge表的组成等。

如何选择存储引擎

MyISAM:如果应用程序通常以检索为主,只有少量的插入、更新和删除操作,并且对事物的完整性、 并发程度不是很高的话,通常建议选择 MyISAM 存储引擎。
InnoDB:如果使用到外键、需要并发程度较高,数据一致性要求较高,那么通常选择 InnoDB 引擎, 一般互联网大厂对并发和数据完整性要求较高,所以一般都使用 InnoDB 存储引擎。
MEMORY:MEMORY 存储引擎将所有数据保存在内存中,在需要快速定位下能够提供及其迅速的访问。MEMORY 通常用于更新不太频繁的小表,用于快速访问取得结果。
MERGE:MERGE 的内部是使用 MyISAM 表,MERGE 表的优点在于可以突破对单个 MyISAM 表大小的限制, 并且通过将不同的表分布在多个磁盘上, 可以有效地改善 MERGE 表的访问效率。

日志文件

MySQL中有八种日志文件,分别是:

  • 重做日志(redo log)
  • 回滚日志(undo log)
  • 二进制日志(binlog)
  • 错误日志(errorlog)
  • 慢查询日志(slow query log)
  • 一般查询日志(general log)
  • 中继日志(relay log)
  • DDL日志 (metadata log)

他们分别都有各自的作用,而且默认情况下,服务器的日志文件都位于数据目录(datadir)中。

常见日志介绍

重做日志(redo log)

确保事务的持久性。防止在发生故障的时间点,尚有脏页未写入磁盘,在重启mysql服务的时候,根据redo log进行重做,从而达到事务的持久性这一特性。
事务开始之后就产生redo log,redo log的落盘并不是随着事务的提交才写入的,而是在事务的执行过程中,便开始写入redo log文件中。
对应的物理文件:
默认情况下,对应的物理文件位于数据库的data目录下的ib_logfile1&ib_logfile2

  • innodb_log_group_home_dir 指定日志文件组所在的路径,默认./ ,表示在数据库的数据目录下。
  • innodb_log_files_in_group 指定重做日志文件组中文件的数量,默认2

关于文件的大小和数量,由以下两个参数配置:

  • innodb_log_file_size 重做日志文件的大小。
  • innodb_mirrored_log_groups 指定了日志镜像文件组的数量,默认1

    回滚日志(undo log)

    保存了事务发生之前的数据的一个版本,可以用于回滚,同时可以提供多版本并发控制下的读(MVCC),也即非锁定读 。undo日志是逻辑格式的日志,在执行undo的时候,仅仅是将数据从逻辑上恢复至事务之前的状态,而不是从物理页面上操作实现的,这一点是不同于redo log的。
    关于MySQL5.7之后的独立undo 表空间配置参数如下:

  • innodb_undo_directory = /data/undospace/ –undo独立表空间的存放目录

  • innodb_undo_logs = 128 –回滚段为128KB
  • innodb_undo_tablespaces = 4 –指定有4个undo log文件

    二进制日志(binlog)

    用于复制,在主从复制中,从库利用主库上的binlog进行重播,实现主从同步。用于数据库的基于时间点的还原。
    内容是以逻辑格式的日志,可以简单认为就是执行过的事务中的sql语句。
    但又不完全是sql语句这么简单,而是包括了执行的sql语句(增删改)反向的信息,也就意味着delete对应着delete本身和其反向的insert;update对应着update执行前后的版本的信息;insert对应着delete和insert本身的信息。
    在使用mysqlbinlog解析binlog之后一些都会真相大白。
    因此可以基于binlog做到类似于oracle的闪回功能,其实都是依赖于binlog中的日志记录。
    对应的物理文件:
    配置文件的路径为log_bin_basename,binlog日志文件按照指定大小,当日志文件达到指定的最大的大小之后,进行滚动更新,生成新的日志文件。

    错误日志(errorlog)

    MySQL 错误日志(error log)记录了 mysqld 启动和停止的相关信息,同时还记录了服务器在启动、停止以及运行期间发生的诊断消息,例如错误、警告和通知等。例如,当 mysqld 发现某个表需要执行自动检查或修复时,它会向错误日志中写入一条消息。

    慢查询日志(slow query log)

    见后面的SQL优化部分。

    事务

    数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完整地执行,要么完全地不执行。
    经典的银行转账例子: A从银行转账1w块钱给B,此时数据库会涉及2个最基本的操作:

  • A银行卡余额减少1w

  • B银行卡余额增加1w

那么这2个操作是一个整体,要么一起成功,要么一起失败,不会存在只有部分成功。例如A银行卡余额减少1w,但是B银行卡余额没有增加1w,这就会出大问题了。

四大特性

原子性(Atomicity)

原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败

一致性(Consistency)

事务按照预期生效,数据的状态是预期的状态。

隔离性(Isolation)

事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务, 不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。

持久性(Durability)

持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的, 接下来即使数据库发生故障也不应该对其有任何影响。

两个操作

提交(commit)

将事务执行结果写入数据库

回滚(rollback)

回滚所有已执行的语句,返回修改之前的数据

锁机制

按照粒度划分

  • 行锁
  • 页锁
  • 表锁

    按照使用方式划分

  • 共享锁

  • 排它锁

    按照思想划分

  • 悲观锁

  • 乐观锁

    行锁

  • record lock :单个行记录删搞的锁。

  • Grap lock :间隙锁锁定一个范围,但不包括记录本身。
  • Next-key lock:锁定一个范围,包括锁记录本身。

    读写锁

  • 共享锁Share lock、又叫读锁。

  • 排它锁(exclusive lock) 又叫写锁。

通过写锁,可以做到读读并行,但不能做到读写、写写并行。

减少死锁的方式

  • 自动死锁检测,优先回滚小事务
  • 超时参数设置(innodb_lock_wait_timeout)
  • 尽量提交事务,小事务不容易产生死锁
  • 加 for update、lock in share mode 读锁时,最好降低事务隔离级别,比如 rc 级别,降低死锁发生概率
  • 事务中涉及多个表,或者涉及多行记录时,每个事务的操作顺序都要保持一致,降低死锁概率,最好用存储过程
  • 通过索引等方式优化 sql 效率,降低死锁概率(目的是减少扫描 / 锁范围,降低概率)

    并发读写带问题

  • 脏读

  • 不可重复度
  • 幻读

    脏读(Drity Read)

    某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。

    不可重复读(Non-repeatable read)

    在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。

    幻读(Phantom Read)

    在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就有几列数据是未查询出来的,如果此时插入和另外一个事务插入的数据,就会报错。

    Mysql的四种隔离级别

    SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。

  • 读未提交

  • 读已提交
  • 可重复度
  • 可串行化

    Read Uncommitted(读取未提交内容)

    在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。

    Read Committed(读取提交内容)

    这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。

    Repeatable Read(可重读)

    这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。

    Serializable(可串行化)

    这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。
    在MySQL中,实现了这四种隔离级别,分别有可能产生问题如下所示:
    mysql相关 - 图1

    MVCC

    定义

    MVCC (Multi­ Version Concurrency Control) 是一种基于多版本的并发控制协议,只有在InnoDB引 擎下存在。另外,这里千万别和Spring MVC联想到一起了。
    MVCC是为了实现事务的隔离性,通过版本号,避免同一数据在不同事务间的竞争,你 可以把它当成基于多版本号的一种乐观锁。当然,这种乐观锁只在事务级别未提交锁和已提交锁时 才会生效。MVCC最大的好处,相信也是耳熟能详:读不加锁,读写不冲突。在读多写少的OLTP 应用中,读写不冲突是非常重要的,极大的增加了系统的并发性能。
    注意:MVCC只在 READ COMMITTEDREPEATABLE READ```两个隔离级别下工作。其他两个隔离级别不和MVCC不兼容, 因为READ UNCOMMITTED总是读取最新的数据行, 而不是符合当前事务版本的数据行。而SERIALIZABLE` 则会对所有读取的行都加锁。
    Mysql默认隔离级别是RR(可重复读),是通过“行锁+MVCC”来实现的,正常读时不加锁,写时加锁,MVCC的实现依赖于:三个隐藏字段Read ViewUndo log 来实现。

    InnoDB MVCC 实现原理

    InnoDBMVCC 的实现方式为:每一行记录都有两个隐藏列:DATA_TRX_IDDATA_ROLL_PTR(如果没有主键,则还会多一个隐藏的主键列)。
    1、DB_TRX_ID:6个字节,记录每一行最近修改他的事务ID
    2、DB_ROLL_PTR:表示指向该行回滚段(rollback segment)的指针,大小为 7 个字节,InnoDB 便是通过这个指针找到之前版本的数据。该行记录上所有旧版本,在 undo 中都通过链表的形式组织。
    3、DB_ROW_ID:行标识(隐藏单调自增 ID),大小为 6 字节,如果表没有主键,InnoDB 会自动生成一个隐藏主键,因此会出现这个列。

    快照读和当前读

    读快照,可以读取数据的所有版本信息,包括旧版本的信息。其实就是读取MVCC中的read_view,同时结合MVCC进行相对应的控制;

    1. select * from table where ?;

    读当前,读取当前数据的最新版本。而且读取到这个数据之后会对这个数据加锁,防止别的事务更改。(分析:在进行写操作的时候就需要进行“当前读”,读取数据记录的最新版本)

    1. select * from table where ? lock in share mode; # 读锁
    2. select * from table where ? for update; # 写锁
    3. insert into table values (…);
    4. update table set ? where ?;
    5. delete from table where ?;

    RC和RR隔离级别下的快照读和当前读:

  • RC隔离级别下,快照读和当前读结果一样,都是读取已提交的最新;

  • RR隔离级别下,当前读结果是其他事务已经提交的最新结果,快照读是读当前事务之前读到的结果。RR下创建快照读的时机决定了读到的版本。

    解决幻读

    对于快照读:通过MVCC来进行控制的,不用加锁。按照MVCC中规定的“语法”进行增删改查等操作,以避免幻读。
    对于当前读:通过next-key锁(行锁+gap锁)来解决问题的。

    索引详情

    索引概念

    索引(在MySQL中也叫“键key”)是存储引擎快速找到记录的一种数据结构。
    索引一般以文件形式存储在磁盘上,索引检索需要磁盘I/O操作
    索引是数据结构,而且是实现了高级查找算法的数据结构
    帮助MySQL高效获取数据的数据结构

    索引分类

  • 普通索引

  • 唯一所有
  • 主键索引
  • 复合索引
  • 全文索引

    MySQL索引结构

  • BTree索引

  • Hash索引
  • full-text全部问索引
  • r-tree索引

在计算机数据结构体系中,为了加速查找的速度,常见的数据结构有两种:

  • Hash哈希结构,例如Java中的HashMap,这种数据组织结构可以让查询/插入/修改/删除的平均时间复杂度都为O(1);
  • Tree 树 结构 , 这种数据组织结构可以让查询/插入/修改/删除的平均时间复杂度都为O(log(n));

Hash这种类型比Tree树这种类型都要更快一些,那为什么MySQL的开发者既使用Hash类型作为索引,又使用了BTREE呢?
确实用Hash索引更快,因为每次都只查询一条信息(重名的雇员姓名也才几条而已),但实际上业务对于SQL的应用场景是:
order by 需要排个序、group by 还要分个组还要比较大小 大于或小于等等
这种情况下如果继续用HASH类型做索引结构,其时间复杂度会从O(1)直接退化为O(n),相当于全表扫描了,而Tree的特性保证了不管是哪种操作,依然能够保持O(log(n))的高效率。

索引实操

创建索引

  1. CREATE [UNIQUE] INDEX indexName ON mytable(columnname(length));
  2. ALTER mytable ADD [UNIQUE] INDEX indexName

删除索引

drop index 索引名 on 表名

查看索引

show index from 表名

索引维护

  • 慢查询
  • 删除不用的索引
  • 查询重复一句冗余索引

    索引优化

  • 使用索引注意点

  • 最左前缀法则
  • 不要在索引列上做任何操作(计算、函数、(自动或手动)类型转 换),会导致索引失效而转向全表扫描
  • 存储引擎不能使用索引中范围条件右边的列
  • 尽量使用覆盖索引(只访问索引的查询(索引列包含查询列)), 减少select *语句
  • mysql在使用不等于(!=或者<>)的时候无法使用索引会导致全 表扫描
  • 字符串不加单引号索引失效

    哪些场景不要建索引

  • 表记录太少

  • 经常增删改的字段
  • where条件中用不到的字段
  • 区分度不够的(性别)

    哪些场景下可建索引

  • 主键自动建议唯一索引

  • 频繁作为查询字段可以考虑建索引
  • 查询中与其他表关联的字段、外键建立索引
  • 查询中统计或分组字段
  • 查询中排序的字段,排序后通过索引去访问将大大提升排序性能

    SQL优化

    explain查询优化器详解

    模拟优化器执行SQL语句,从而知道MySQL是 如何处理你的SQL语句的,分析 你的查询语句或者表结构的性能瓶颈。

    使用方式

    在 select 语句之前增加 explain 关键字,MySQL 会在查询上设置一个标记, 执行查询时,会返回执行计划的信息,而不是执行这条SQL。

    1. explain select * from user where id = 1001;
    2. --为了方便查看,可以在语句后面加一个\G,表示按字段顺序展开
    3. explain select * from user where id = 1001 \G

    作用

  • 表的读取顺序

  • 数据读取操作的操作类型
  • 哪些索引可以使用
  • 哪些索引被实际使用
  • 表之间的引用
  • 每张表有多少行被优化器查询

    explain变种

    explain extended

    会在 explain 的基础上额外提供一些查询优化的信息。 紧随其后通过 show warnings 命令可以 得到优 化后的查询语句,从而看出优化器优化了什么

    explain partitions

    相比 explain 多了个 partitions 字段, 如果查询是基于分区表的话,会显示查询将访问的分区。

    explain 结果列详情

    id

    id列的编号是 select 的序列号,有几个 select 就有几个id,并且id的顺序是按 select 出现的顺序增长 的。
    id列越大执行优先级越高,id相同则从上往下执行,id为NULL最后执行。

    select_type

    SIMPLE:简单查询。查询不包含子查询和union
    PRIMARY:最外层SELECT
    UNION:第二层,在SELECT之后使用了UNION。
    DEPENDENT UNION:UNION语句中的第二个SELECT,依赖于外部子查询。
    UNION RESULT:UNION的结果
    SUBQUERY:子查询中的第一个SELECT。
    DEPENDENT SUBQUERY:子查询中的第一个SELECT,取决于外面的查询。
    DERIVED :导出表的SELECT(FROM子句的子查询)
    MATERIALIZED:物化子查询
    UNCACHEABLE SUBQUERY:无法缓存结果的子查询,必须为外部查询的每一行重新计算
    UNCACHEABLE UNION :UNION 属于不可缓存的子查询的第二个或后一个选择

    table

    union

    <unionM,N,...>:该行指的是id值为M和id值为N的并集。

    derived

    <derivedN>:该行是指用于与该行的派生表结果id的值 N。 例如,派生表可以来自FROM子句

    subquery

    <subqueryN>:该行指的是id 值为的行的具体化子查询的结果N

    type

    NULL

    MySQL在优化过程中分解语句,执行时甚至不用访问表或索引, 例如从一个索引列里选取最小值可以通过单独索引查找完成。

    system

    该表只有一行(如:系统表)。这是const连接类型的特例

    const

    该表最多只有一个匹配行,在查询开头读取。因为只有一行, 所以优化器的其余部分可以将此行中列的值视为常量。 const表非常快,因为它们只读一次。SELECT * FROM tbl_name WHERE primary_key=1;

    eq_ref

    除了 system和 const类型之外,这是最好的连接类型。 当连接使用索引的所有部分且索引是 索引PRIMARY KEY或UNIQUE NOT NULL索引时使用它。

    ref

    表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值

    fulltext

    使用FULLTEXT 索引执行连接。

    ref_or_null

    该联接类型如同ref,但是添加了MySQL可以专门搜索包含NULL值的行。SELECT * FROM ref_table WHERE key_column IS NULL;

    index_merge

    该指数合并访问方法检索与多行 range扫描和他们的结果合并到一个。 此访问方法仅合并来自单个表的索引扫描,而不扫描多个表。

    unique_subquery

    该类型替换了下面形式的IN子查询的 ref:value IN (SELECT primary_key FROM single_table WHERE some_expr) unique_subquery是一个索引查找函数, 可以完全替换子查询,效率更高。【不常用】

    index_subquery

    该联接类型类似于unique_subquery。可以替换IN子查询,但只适合下列形式的子查询中的非唯一索引:value IN (SELECT key_column FROM single_table WHERE some_expr)。【不常用】

    range

    只检索给定范围的行,使用一个索引来选择行。

    index

    该联接类型与ALL相同,Full Index Scan,index与ALL区别为index类型只遍历索引树。 这通常比ALL快,因为索引文件通常比数据文件小。

    ALL

    Full Table Scan, MySQL将遍历全表以找到匹配的行。

    各个类型对比

    结果值从好到坏依次是:Null > system > const > eq_ref > ref > ref_or_null> index_merge > unique_subquery > index_subquery > range > index > ALL

    建议类型

    一般来说,得保证查询至少达到range级别,最好能达到ref。

    possible_keys

    表示查询时,可能使用的索引。( MySQL能使用哪个索引在该表中找到行)

    key

    实际使用的索引(键),必然包含在possible_keys中。如果没有选择索引,索引是NULL。 要想强制MySQL使用或忽视possible_keys列中的索引, 在查询中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。

    key_len

    索引的长度 ( 使用的字节数 )。如果索引是NULL,则长度为NULL。不损失精确性的情况下,长度越短越好 。key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的。

    ref

    使用哪个列或常数,与索引一起被用于从表中查找索引列上的值。 ( 列与索引的比较,表示上述表的连接匹配条件。) 显示索引的哪一列被使用了,如果可能得话,是一个常数。

    rows

    MySQL认为它执行查询时必须检查的行数。( 扫描出的行数 [估算的行数 ]。)

    filtered

    通过表条件过滤出的行数的百分比估计值。

    Extra

    Mysql执行情况的描述和详细说明。信息 Extra 的常用值说明:
    Using filesort:使用了文件进行排序,效率较低
    Using index:使用了覆盖索引,效率较高
    Using temporary:使用了临时表
    Using where:使用了 where 条件

    慢查询

    获取慢查询的方式

    在工作中,我们发现慢查询一般有 2 个途径,一个是被动的,一个是主动的。估计大部分人都是被动的在处理慢查询,被动的就是当业务人员反馈某个查询界面响应的时间特别长,你才去处理。主动的就是通过通过分析慢查询日志来主动发现执行效率缓慢的 sql 语句,或者通过 information_schema.processlist 实时查询执行缓慢的 sql。

    分析慢查询日志

    分析慢查询日志的步骤:

  • 通过命令查看数据库是否开启慢查询日志:show variables like 'slow_query_log';

  • 设置开启慢查询日志:set global slow_query_log=on;
  • 没有命中索引的查询记入慢查询日志:set global log_queries_not_using_indexes = on;
  • sql 语句超过多少秒记入慢查询日志:set global long_query_time=1;
  • 查看慢查询日志保存为准:show variables like 'slow_query_log_file';
  • 直接打开日志进行查看:vi + file 路径
  • 慢查询日志工具:mysqldumpslow -s at -t 15 file 路径

    慢查询日志参数介绍

    Time:日志记录时间
    User@Host:执行的用户与主机
    Query time:查询耗费时间
    Lock time:锁表时间
    Rows_sent:返回的结果行数
    Rows_examined:扫描的记录行数
    Set timestamp:sql 语句执行的时间
    sql 语句表示执行的具体语句。

    分析 information_schema.processlist

    1. SELECT id,user,host,DB,command,time,state,info
    2. FROM information_schema.processlist
    3. WHERE TIME>=30;
    查询当前服务器执行超过 30 秒的 sql,可以通过定时任务周期性的来执行这个 sql,就能找到查询缓慢的 sql 语句。
    通过以上两种方式找出查询较慢的 sql,进行优化即可。

    MySQL锁优化

    表级锁

    对整张表加锁。 开销小,加锁快; 不会出现死锁; 锁定粒度大,发生锁冲突的概率最高,并发度最低。

    行级锁

    对某行记录加锁。 开销大,加锁慢; 会出现死锁; 锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

    页面锁

    开销和加锁时间介于表锁和行锁之间; 会出现死锁; 锁定粒度界于表锁和行锁之间,并发度一般。

    MySQL 常用调优策略

    mysql相关 - 图2