MySQL Optimization




MySQL架构设计与优化最佳实践




               彭立勋   	

      http://www.PengLiXun.com/  	

        PengLiXun@gmail.com
Topics of Discussion(上午)

l    MySQL缺陷与适用场景
l    MyISAM vs InnoDB
l    MyISAM与InnoDB性能调优
l    MySQL Server性能调优
l    MySQL Join算法
l    MySQL 优化器特性
l    索引设计杂谈
Topics of Discussion(下午)

l    编写高性能SQL
l    设计高性能Schema
l    数据备份与    策略
l    数据拆分方案
l     制设计方案
l    典型故障案例分析
l    构建高性能高可用可扩展MySQL集群
Next Topic




MySQL缺陷与适用场景
MySQL架构
MySQL缺陷

Ø    MySQL没有Hash Join à 不适合无索引大数据
      量数据分析
Ø    MySQL没有完整的CBO à 不可依赖MySQL选
      择最优的执行计 ,不可依赖MySQL自行优化
      SQL
Ø    MySQL没有备块级   制 à 不可依赖MySQL保证
      备库的实时一致性
Ø    MySQL无法有效利用主机资源 à 单机单实例会
      严重的浪费机器性能
MySQL适用场景

ü    应用会产生庞大连接数(Thread)
ü    可以依赖索引完成的数据分析(Covering
      Index)
ü    高并发查询(MyISAM)
ü    短小的在线事务处理(InnoDB)
ü    简单条件查询(HandlerSocket)
ü    数据拆分(Free)
Next Topic




MyISAM vs InnoDB
MyISAM的优势和劣势
      优势                   劣势
小内存下更快速的查询:	
     数据文件容易损坏:	
  
因为没有复杂的MVCC多版本控   因为数据回写机制没有原子性
制机制               保证
更小的数据文件:	
        没有事务保证	
  
因为数据文件中没有额外的行
锁和事务信息
非常容易冷备份:	
        只有索引缓存
因为每张表三个文件,没有额
外的全局信息,拷贝文件即可
备份
                  表级锁,并发差
InnoDB的优势和劣势
       优势                 劣势
行级锁,并发高:	
         小内存下纯读应用不如MyISAM
只锁定被访问到的行,并不影      快
响对不同行的操作。
ACID事务保证           备份操作更复杂:	
  
                   因为存在事务信息,多版本信
                   息,不能简单的拷贝数据文件。
                   但是支持热备份。
所有数据文件均可缓存:	
      更占用磁盘空间:	
  
InnoDB主键索引即数据,缓存   因为行结构中包含事务信息和
索引就包括了缓存数据         行锁信息,另外有UNDO和REDO
                   事务日志
读写互不阻塞:	
  
得益于MVCC多版本控制,读数
据并不需要锁定修改,可以在
UNDO日志中找到旧版本。
索引对比
        MyISAM                   InnoDB
B-­‐Tree结构,不能自动Balance   B+Tree结构,自动Balance
堆表:	
                    索引组织表:	
  
数据文件独立于索引,数据文            数据文件以主键顺序组织,主
件以插入顺序组织,以物理位            键即数据,非主键索引通过主
置为引用键,索引通过物理位            键查找数据行。所以主键插入
置查找数据行。                  无序时性能极差。
                         支持自适应哈希:	
  
                         当InnoDB发现表上经常存在某
                         些唯一键上的KV查询时,会自
                         动把这部分数据生成到自适应
                         哈希表中,可进行KV查询优化。
                         主键索引包含事务信息:	
  
                         主键索引上唯一确定了一行数
                         据,所以事务信息包含在主键
                         索引上。如果没有显示定义主
                         键索引,将自动建立隐含主键。
MyISAM场景

ü    数据仓库:大量读,批量写,可共享数据文件
ü    日志系统:只有插入没有修改,简单查询
ü    读为主的应用:没有写或写   少,几乎不阻塞读
ü    低并发的应用:表锁基本不影响并发
ü    大数据批量导入:避免InnoDB索引组织表主键
      乱序时插入慢
InnoDB场景

ü    高并发的应用:例如消息队列
ü    可靠性要求高的应用:例如用户登陆信息
ü    数据一致性要求高,宕机可     :例如用户数据
ü    要求事务和约束的应用:例如有数据   联的系统
ü    需要高性能缓冲写的应用:例如短时间内大量
      UPDATE
同时使用MyISAM / InnoDB

Ø    不可共享缓存空间
Ø    SQL包含   表将失去事务意义


总之不推荐这  表同时存在同一实例,除非:
 表没有 联,MyISAM表只做备份冗余。
但是,Master用InnoDB,Slave用MyISAM某些情
况下是不错的方案。
Next Topic




MyISAM和InnoDB的优化
MyISAM优化




      l    INSERT 优化
l          Key Buffer 优化
MyISAM 插入

Ø    定⻓长表优于变⻓长表,即尽可能不定义变⻓长字段
Ø    数据存储按插入顺序,除非删除了数据产生的空
      洞可以插入新行 或 使用了并发插入
Ø    可以加大bulk_insert_buffer_size变量,采用
      ”INSERT INTO table VALUES (),(),...” 的批量插
      入语法,或者采用LOAD DATA语法
MyISAM 并发插入

虽然MyISAM是表锁,但是如果只在数据文件末尾
插入数据,是可以并发的,通过concurrent_insert
变量控制:
n    0 – (Off):不允许并发插入
n    1 – (Default):如果数据文件没有空洞,可以在数据文件末尾并发
      插入
n    2 – Enable:如果表在被使用则在数据文件末尾并发插入,如果
      表没有被使用则一样在文件中找空洞插入 [建议]
Key Buffer 原理
Key Buffer 介绍

作用:缓存MyISAM的索引,默认16M
相       变量:key_buffer_size(Key Buffer大小)
相       状态量:
n    key_read/write_requests(索引逻辑读/写)
n    key_reads/writes(索引物理读/写,即磁盘读/写)
n    key_blocks_used/unused(已使用/未使用的缓冲块)
n    key_blocks_not_flushed(未回写的脏缓冲块)
Key Buffer 监控

Ø    命中率 = (requests - reads) / requests


Ø    利用率 = key_blocks_used / (key_blocks_used +
      key_blocks_unused)


Ø    赃⻚页率 = key_blocks_not_flushed /
      (key_blocks_used + key_blocks_unused)
Key Buffer 注意

key_buffer_size不能 闭,因为至少mysql系统库
全部用的MyISAM表,就算整个实例都是InnoDB
表,也应该设置key_buffer_size。
多段 Key Buffer

     少Key Buffer锁的竞争:
将Key Buffer拆分为多个段,分                                        缓冲不同的表。

!    root@localhost : (none) 04:16:36> SET GLOBAL hot_cache.key_buffer_size=128*1024;
!    Query OK, 0 rows affected (0.00 sec)
!    root@localhost : mysql 04:28:05> CACHE INDEX user IN hot_cache;
!    +------------+--------------------+----------+----------+
!    | Table      | Op                 | Msg_type | Msg_text |
!    +------------+--------------------+----------+----------+
!    | mysql.user | assign_to_keycache | status   | OK       |
!    +------------+--------------------+----------+----------+
!    1 row in set (0.00 sec)
预载入 Key Buffer

     少Key Buffer预热时间:
预先载入指定表的索引到指定缓冲段。

!    root@localhost : mysql 04:28:13> LOAD INDEX INTO CACHE user;
!    +------------+--------------+----------+----------+
!    | Table      | Op           | Msg_type | Msg_text |
!    +------------+--------------+----------+----------+
!    | mysql.user | preload_keys | status   | OK       |
!    +------------+--------------+----------+----------+
!    1 row in set (0.00 sec)
Key Buffer 建议

最好可以保证Key Buffer Size大于目前所有需要访
问的表MYI文件大小总和。
如无法保证则一般分三个区给不同业务类型的表:
n    静态缓冲区, 少或不修改的表,这个区的Key Buffer访问没有
      写锁,都是Shared Read Lock。
n    修改缓冲区,大量修改或UPDATE的表,这个区的Key Buffer竞
      争严重,限制竞争在一个区。
n    普通缓冲区,除大量修改和只读表以外的正常读写比例的表。
刷新 Key Buffer

内存中缓存的索引块,有时候并不会及时刷新到磁盘上,所以对于
正在运行的数据表的索引文件(MYI)一般都是不完整的。如果此时
拷⻉贝或者移动这些索引文件。多半会出现索引文件损坏的情况。可
以通过Flush table命令来将Key Buffer中的block都flush到磁盘上。

!    mysql> flush tables;
!    +------------------------+----------+
!    | Variable_name          | Value    |
!    +------------------------+----------+
!    | Key_blocks_not_flushed | 0        |   #所有修改的block都已经被flush了
!    | Key_blocks_unused      | 0        |
!    | Key_blocks_used        | 14497    |
!    | Key_read_requests      | 38333936 |
!    | Key_reads              | 207      |
!    | Key_write_requests     | 8819898 |
!    | Key_writes             | 1255245 |
!    +------------------------+----------+
InnoDB优化




l    Buffer Pool 调优
            l    日志 调优
      l          XtraDB调优
InnoDB 简介

Ø    InnoDB是MySQL上应用最广的事务引擎,淘宝
      商品库、阿里巴巴商品信息全部 在InnoDB
      上。
Ø    InnoDB目前包含 个版本,InnoBase和InnoDB
      Plugin,5.1之前只有InnoBase,5.1之后 始有
      InnoDB Plugin。另外还有基于MySQL Server和
      InnoDB Plugin优化的Percona Server和
      XtraDB。
Ø    前阿里巴巴正在使用的就是Percona+XtraDB高
      性能版本,完全兼容MySQL。
InnoDB 文件结构
InnoDB 数据文件组织

Ø    最基本的修改单位是Row
Ø    最基本的IO单位是Page,包含2个以上Row
Ø    最基本的分配单位是Extent,包含64个Page
Ø    InnoDB的索引都是指到Page no为止,为了B-
      Tree保持高效,InnoDB要求一个Page内至少有
      2行数据,所以我们建议每行数据设计⻓长度不要
      超过4K,这样我们可以方便的 移到最平衡的
      8K⻚页大小。
InnoDB 数据文件组织
InnoDB 索引结构
InnoDB 主线程
Buffer Pool

InnoDB最重要的一块内存就是Buffer Pool,它包含
了几乎所有InnoDB数据和索引的缓冲,主要有:
n    数据缓存(主键缓存)
n    索引缓存(非主键索引缓存)
n    自适应哈希
n    数据字典
n    ⻚页哈希等
Buffer Pool 设置

innodb_buffer_pool_size设置Buffer Pool的大小。
建议将除了连接使用的内存,操作系统使用的内存
之外的全部内存给Buffer Pool,不要靠操作系统的
OS Cache来缓冲缓存数据。
经验参考值:
n    48G内存:32G Buffer Pool
n    32G内存:20G Buffer Pool
n    24G内存:16G Buffer Pool
n    16G内存:10G Buffer Pool
Buffer Pool 赃⻚页比例

Buffer Pool同时担任缓冲和缓存工作,因此Buffer
Pool中会产生赃⻚页,即在内存中修改了但未刷新到
磁盘上的⻚页。
innodb_max_dirty_pages_pct变量可以配置最多允
许多少比例的赃⻚页在内存中,超过比例将触发刷
新。
经验参考值:
n    75%适用于绝大部分情况,Google/Facebook/Alibaba均使用
n    5%适用于非常在意宕机快速        的情况,Taobao/Alipay使用
InnoDB 事务安全

Ø    REDO LOG:所有变更内容全部写入REDO LOG。一
      旦数据库Crash,将redo中已经标记为commit状态的事
      务全部重做到数据文件,将没有标记为commit的事务在
      数据文件中回 。
Ø    UNDO LOG:所有数据被修改的旧版本数据和版本号
      写入undo,当需要回 事务时,在undo中找到旧版本
      信息,覆盖数据文件。
Ø    Double Write Buffer:所有的⻚页级回写,都先将⻚页写
      入Double Write Buffer区域, 认CheckSum与内存中
      的⻚页一致,再正式写入数据文件中,如果数据库Crash,
      数据文件中的⻚页没有回写完成,可以从Double Write
      Buffer中读取正 的⻚页重写。
InnoDB 日志
Log Buffer

Ø    事务安全使得InnoDB会产生事务日志,包括
      REDO和UNDO。日志回写到磁盘会有一个Log
      Buffer缓冲日志写
Ø    innodb_log_buffer_size设置事务日志缓冲大小
Ø    经验参考值:
      n    默认只有1M,存在大事务或者高并发情况,建议设置8~16M
InnoDB 事务提交

Ø    Log Buffer中缓冲的日志在检查点或事务提交时
      回写文件。
Ø    Buffer Pool中的⻚页在检查点时回写磁盘。
Ø    innodb_flush_log_at_trx_commit控制回写:
      n    0: 每秒刷新日志到文件(不是磁盘)
      n    1: 每次事务提交刷新日志到磁盘 [default]
      n    2: 每次事务提交刷新日志到文件,每秒同步到磁盘 [建议]
日志大小设置

Ø    更大的事务日志意味着什么?
      n    可以记录更大的事务信息 [可以重做更大的事务]
      n    可以记录更⻓长的事务历史 [可以重做更多的事务]
      n    更少的检查点 [   少磁盘IO]
      n    更⻓长的   时间 [宕机重启时间更⻓长]
Ø    经验参考值:
      n    64M的日志5分钟以内就可以
      n    1G的日志全部重做需要30分钟以上
      n    我们设置为3个1G的日志
InnoDB 锁机制

Ø    InnoDB锁算法:写数据有索引时采用间 锁(Gap
      Lock),无索引时采取表锁。读利用MVCC机制,不加
      锁。所以DELETE / UPDATE的条件没有加索引后果很
      严重。
      n    WHERE pk = 1,锁定pk=1的行,不可以修改pk=1的数据。
      n    WHERE pk > 1,锁定pk>1后的所有行,不可以操作pk>1的
            数据,包括插入删除修改。
      n    WHERE pk BETWEEN 1 AND 10,锁定1~10内所有行,不
            可以操作pk在1~10的所有数据。
Ø    InnoDB锁结构:通过为每个⻚页生成一个锁定位图,判
      定行是否被锁定,每⻚页的第一行和最后一行是系统行,
      如果被锁定说明整⻚页被锁。
InnoDB MVCC

Ø    多版本实现:
!     数据旧版本写入UNDO LOG,数据Page中保留最新数据和版本
      号,当Query发现Page中的数据版本太新,则顺着undo log
      pointor到UNDO LOG寻找到需要的历史版本,所以读取数据不
      需要加锁。

Ø    事务可⻅见性判定:
!     MVCC需要做版本可⻅见性判定,以 认一个版本号的数据是否可
      以为当前事务所⻅见。InnoDB使用read view结构来实现版本可⻅见,
      一个事务起始时,会被分配一个read view,包含当前正在执行的
      事务号,根据不同的隔离级 需求,可以依据read view的事物号
      信息来判定行数据可⻅见性。
!     例如MySQL默认隔离级 为可重 读(Repeatable Read),那
      么只要读取到的数据事务号大于read view中的最小事务号,就是
      不可⻅见的。
自适应哈希

Ø    自适应哈希是InnoDB的天才设计之一,InnoDB存储引
      擎会智能的监控对表上索引的查找,如果观察到建立哈
      希索引可以带来速度的提升,则自动建立哈希索引。
Ø    自适应哈希索引通过缓冲池的B+树构造而来,因此建立
      的速度很快。并不将整个表都建哈希索引,InnoDB存
      储引擎会自动根据访问的频率和模式来为某些⻚页建立哈
      希索引。
!     -------------------------------------
!     INSERT BUFFER AND ADAPTIVE HASH INDEX
!     -------------------------------------
!     Ibuf: size 2249, free list len 3346, seg size 5596,
!     374650 inserts, 51897 merged recs, 14300 merges
!     Hash table size 4980499, node heap has 1246 buffer(s)
!     1640.60 hash searches/s, 3709.46 non-hash searches/s
Insert Buffering

Ø    Insert Buffer是InnoDB的另一个天才设计,当有数据更
      新时,不立刻更新Index叶子节点的Page,而是将这些
      更新缓冲起来,因此对同一个Page的更新可以一起回
      写,这 将离散IO整合为顺序IO的设计,非常适合现在
      的SSD盘。
Ø    但是Insert Buffer只对非唯一索引起作用,因为在做
      Merge之前检查值是否唯一是不可能的。
!     -------------------------------------
!     INSERT BUFFER AND ADAPTIVE HASH INDEX
!     -------------------------------------
!     Ibuf: size 7545, free list len 3790, seg size 11336,
!     8075308 inserts, 7540969 merged recs, 2246304 merges
InnoDB 优化实践

ü    尽可能使用小的主键: 少非主键索引大小
ü    按主键顺序插入数据:避免主键节点分裂
ü    加大日志大小:避免过多的检查点
ü    避免大事务回 :会产生大量的随机IO,拆分事
      务
ü    避免零散的插入:尽可能批量插入
ü    尽量 用前缀索引:InnoDB不会压缩索引
InnoDB 其他重要变量

Ø    innodb_additional_mem_pool_size:内部元信
      息缓存,16M足以胜任任何情况
Ø    innodb_support_xa:是否支持 段提交协议,
      没有必要 启, 闭可获得10%的性能提升。
InnoDB Plugin 增强

Ø    IO容量:innodb_io_capacity
Ø    IO线程数:
      n    innodb_read_io_threads(预读)
      n    innodb_write_io_threads(赃⻚页回写)
      n    innodb_use_purge_thread(清理UNDO)

Ø    可变⻚页大小(innodb_page_size)
Ø    可配置多回             段(innodb_extra_rsegments)
Ø    Compressed表结构:CPU换IO,压缩空间
Ø    扩展INFORMATION_SCHEMA
InnoDB Plugin 配置

Ø    建议经验值:
      n    innodb_io_capacity = 磁盘数*200 (SSD>4000)
      n    innodb_read_io_threads = 4
      n    innodb_write_io_threads = CPU核数/实例数*2
      n    innodb_use_purge_thread = 回   段/2
      n    innodb_page_size = 8K (全表扫描16K,小数据4K)
      n    innodb_extra_rsegments = 平均并发插入数,但不要超过
            CPU数*2
      n    字符型列为主的表设置Compressed表结构
Next Topic




MySQL Server 性能调优
Table Cache 介绍

Table Cache是表高速缓存,缓存表文件句柄,每
张表被访问前线程都需要获得它的文件句柄,如果
在表缓存中命中,则不需要再次打 文件,直接从
缓存中获取文件句柄。
!    root@localhost : (none) 12:36:11> show global status like 'open%tabl%';
!    +--------------------------+-------+
!    | Variable_name            | Value |
!    +--------------------------+-------+
!    | Open_table_definitions   | 18    |
!    | Open_tables              | 12    | # 当前打 的表数量
!    | Opened_table_definitions | 24    |
!    | Opened_tables            | 24    | # 表曾经被打 多少次
!    +--------------------------+-------+
Table Cache 调优

建议经验值:
n     一般table_open_cache=1024适用于大部分情况。
n     如果opened_tables不断增⻓长,应调大table_open_cache。

查看目前打                     的表:
  !    root@localhost : (none) 12:39:24> show open tables;
  !    +----------+--------------+--------+-------------+
  !    | Database | Table        | In_use | Name_locked |
  !    +----------+--------------+--------+-------------+
  !    | mysql    | servers      |      0 |           0 |
  !    | mysql    | event        |      0 |           0 |
  !    | test     | t3           |      0 |           0 |
  !    | mysql    | procs_priv   |      0 |           0 |
  !    | mysql    | db           |      0 |           0 |
  !    | mysql    | host         |      0 |           0 |
  !    | test     | t2           |      0 |           0 |
  !    +----------+--------------+--------+-------------+
Query Cache 介绍

Query Cache是独立于存储引擎的SQL级缓存,它
实际上是SQL文本的MD5值和SQL执行结果的哈希
键对。因此Query Cache的使用受到很大限制:
n    SQL文本必须完全相同,包括大小写,空格
n    存在不   定函数的SQL不被缓存,例如NOW()
n    PREPARE方式执行的SQL不被缓存
n    自定义函数,存储过程,视图均不能被缓存
Query Cache 设置

Ø    配置参数:
      n    query_cache_type:
            n    OFF:不对SELECT结果集缓存
            n    ON:对SELECT结果集缓存 [默认]
            n    DEMAND:只有当SELECT语句中显示写明“SQL_CACHE”时候才对
                  结果集缓存 [推荐]
      n    query_cache_size:Query Cache大小
      n    query_cache_limit:可缓存的最大结果集,默认1MB
      n    query_cache_min_res_unit:内存分配最小单位,默认4KB
Ø    经验参考值:
      n    InnoDB应该 闭QCache,因为InnoDB有MVCC
      n    MyISAM如果 实数据很少变化,可以测试常用SQL的结果集
            大小,设置为最常用的SQL的结果集
Query Cache 监控

Ø    命中率 = Qcache_hits / (Com_select + Qcache_hits)
Ø    利用率 = (query_cache_size -
      Qcache_free_memory) / query_cache_size
Ø    碎片率 = Qcache_free_blocks / Qcache_total_blocks
!     mysql> show status like ‘Qcache%’;
!     +-------------------------+----------+
!     | Variable_name            | Value    |
!     +-------------------------+----------+
!     | Qcache_free_blocks       | 1        |   #   缓存中有多少未被使用空闲的内存块
!     | Qcache_free_memory       | 16768400 |   #   可用的缓存空间
!     | Qcache_hits              | 0        |   #   缓存命中的次数
!     | Qcache_inserts           | 0        |   #   被缓存的查询次数,也就是没有命中的次数
!     | Qcache_lowmem_prunes     | 0        |   #   由于内存不足导致被删除的缓存条目数量
!     | Qcache_not_cached        | 64       |   #   无法被缓存的查询的数量
!     | Qcache_queries_in_cache | 0         |   #   当前被cache的查询数量
!     | Qcache_total_blocks      | 1        |   #   当前使用的内存块的数量
!     +-------------------------+----------+
Query Cache 优化

Ø    如果Qcache_lowmem_prunes很大,说明Query
      Cache大小不足
Ø    如果命中率很低,说明Query Cache没有必要
Ø    如果Qcache_free_blocks很大,说明Query
      Cache需要整理碎片,Flush Query Cache。
Ø    query_cache_min_res_unit =
      (query_cache_size - Qcache_free_memory) /
      Qcache_queries_in_cache
Thread Cache 介绍

每个连接到MySQL都需要创建一个线程,Thread
Cache就缓存了已经断 的连接线程描述符,如果
创建线程前检测到Thread Cache还有可用线程,
就不需要新创建,从缓存中获取即可。
!    void create_thread_to_handle_connection(THD *thd) {
!        if (cached_thread_count > wake_thread) { //看线程缓存(thread_cache)中有否空
     余的线程
!         thread_cache.append(thd);
!          pthread_cond_signal(&COND_thread_cache); // 有的话则唤醒一个线程来用
!      } else {
!          threads.append(thd);
!          pthread_create(&thd->real_id,&connection_attrib,
     handle_one_connection, (void*) thd))); //没有可用空闲线程则创建一个新的线程
!      }
!    }
Thread Cache 配置

Ø    经验建议值:
      n    thread_cache_size = 1024 适应绝大部分场景
      n    并发连接数很少时,相应降低配置
Ø    优化建议:
      n    如果threads_created增⻓长很快,应该调大thread_cache_size
            的值
连接数

Ø    配置变量:
      n    max_connections限制全局连接数
      n    max_user_connections限制每个用户的连接数
Ø    建议经验值:
      n    max_user_connections:没有特殊情况不用限制
      n    max_connections:默认100。一般1000个连接可以满足绝大
            部分应用,但是应注意,每个连接都会申请线程级内存(包
            括Sort Buffer,Join Buffer,Binlog Cache等),要考虑内存
            是否可以承受。
      n    我们设置为3000,因为很少有连接需要排序和JOIN,一般不
            需要多少线程级内存。
Sort Buffer 介绍

Ø    每个线程需要排序的时候都会申请一块Sort
      Buffer,线程第一次使用的时候分配,连接断
      后线程不销毁则内存不释放。
Ø    如果要排序的数据大于Sort Buffer将会用归并排
      序法分块排序,超出内存部分写入磁盘暂存。
Ø    sort_buffer_size 根据需要排序的数据集大小而
      定,可以按照Session在连接内重新设定。
Sort Buffer 监控

Ø    Sort_merge_passes:归并排序次数,这个值很大说明
      Sort Buffer不 ,很多排序数据写到磁盘上了
Ø    Sort_rows:被排序数据的行数
Ø    Sort_range:通过索引选择了一个范围的数据后排序
Ø    Sort_scan:通过全表扫描进行排序的次数,这个值很
      大应该优化索引避免排序
Join / Read Buffer

Ø    join_buffer_size:JOIN时如果不能用到索引就
      会使用Join Buffer进行JOIN,最好优化索引避免
      使用Join Buffer。
Ø    read_buffer_size:全表扫描时缓存读取的数
      据,最好通过索引避免全表扫描。
Tmp Table 介绍

当执行计 中有Using temporary时表示要采用临时
表完成SQL。
tmp_table_size配置在内存中分配临时表的最大限
制,超过部分将建到磁盘上。
!    root@localhost : test 04:21:53> explain select * from t1,t2 where t1.info=t2.info
     order by t1.id;
!    +----+-------------+-------+------+---------------+------+---------+--------------
     +------+---------------------------------+
!    | id | select_type | table | type | possible_keys | key | key_len | ref            |
     rows | Extra                           |
!    +----+-------------+-------+------+---------------+------+---------+--------------
     +------+---------------------------------+
!    | 1 | SIMPLE       | t2    | ALL | NULL           | NULL | NULL    | NULL          |
     2 | Using temporary; Using filesort |
!    | 1 | SIMPLE       | t1    | ref | idx            | idx | 32       | test.t2.info |
     1 | Using index                     |
!    +----+-------------+-------+------+---------------+------+---------+--------------
     +------+---------------------------------+
Tmp Table 监控

Ø    重要状态:
      n    Created_tmp_disk_tables:   件磁盘临时表数量
      n    Created_tmp_tables:创建临时表数据
Ø    建议经验值:
      n    一般tmp_table_size设置为64M足以应对大部分情况
      n    如果Created_tmp_tables增⻓长很快,最好优化SQL和索引避
            免临时表
      n    如果Created_tmp_disk_tables增⻓长很快,应调大
            tmp_table_size的值
Next Topic




MySQL Join 算法
JOIN 算法概述

Ø    MySQL只有一                     JOIN算法:Nested Loop Join
Ø    Nested Loop Join:选择一张驱动表,用                      联字
      段循环匹配被驱动表
!     FOR EACH ROW row1 IN table1 {
!         FOR EACH ROW row2 IN table2 IF(   联条件) {
!             result := row1 || row2;
!             OUTPUT result;
!         } }

Ø    支持条件下推(Push Down):先用条件过滤驱动
      表和被驱动表,再把过滤后的结果集进行JOIN
内连接

Ø    假定我们有一个如下形式的表T1、T2、T3的联
      接查询:SELECT * FROM t1 INNER JOIN t2 ON c1(t1,t2) INNER JOIN
      t3 ON c2 (t2,t3) WHERE c(t1,t2,t3)

Ø    MySQL首先由优化器判定JOIN顺序,假设生成
      执行计 为t1驱动t2驱动t3:
!     FOR EACH ROW row1IN t1 IF (c(t1)){
!         FOR EACH ROW row2 IN t2 IF (c1(t1,t2) && c(t2)) {
!             FOR EACH ROW row3 IN t3 IF (c2(t2,t3) && c(t3)) {
!                 result := row1 || row2 || row3;
!                 OUTPUT result;
!                 } } }

Ø    将行传给下一层被驱动表匹配时,先用条件进行
      过滤, 少参与JOIN的行数
外连接

Ø    SQL同前例:
!     FOR EACH ROW row1 IN t1 IF c1(t1) {
!         BOOL f1 := FALSE;
!         FOR EACH ROW row2 IN t2 IF c1(t1,t2) AND (f1 ? c2(t2) : TRUE) {
!             BOOL f2 := FALSE;
!             FOR EACH ROW row3 IN t3 IF c2(t2,t3) AND (f1&&f2 ? c3(t3) : TRUE) {
!                 IF (f1&&f2 ? TRUE : (c2(t2) AND c3(t3))) {
!                     result := row1 || row2 || row3; OUTPUT result; }
!                  f2 := TRUE; f1 := TRUE; }
!             IF (!f2) {
!                 IF (f1 ? TRUE : c2(t2) && c(t1,t2,NULL)) {
!                     result := row1 || row2 || NULL; OUTPUT t; }
!                 f1 := TRUE; } }
!        IF (!f1 && c(t1,NULL,NULL)) {
!             result := row1||NULL||NULL; OUTPUT t; } }

Ø    经过测试,三张10000行的表用外连接平均比内
      连接慢0.01s,内连接效率高于外连接。
Next Topic




MySQL 优化器特性
MySQL 优化器概述

Ø    MySQL优化器属于RBO(基于规则的优化器),
      5.1版本后带有少量的CBO(基于 销的优化器)
      特性。
Ø    MySQL优化器缺陷:
      n    不支持函数索引:WHERE func(col) = x 无法使用col字段上
            的索引
      n    不支持索引过滤 (Index Filter) 但支持索引覆盖扫描:
             n    SELECT col1,col2,col3,col4 FROM t WHERE col1 > x AND col2 < y AND col3 >z; 无法利用
                   (col1,col2,col3)索引过滤col2和col3,需要回表查询。
             n    SELECT col1,col2,col3 FROM t WHERE col1 > x AND col2 < y AND col3 >z; 可以利用
                   (col1,col2,col3)索引扫描到col1,col2,col3的值不用回表。
      n    无法自动优化子查询:MySQL无法将子查询优化为JOIN方式,
            而是将子查询存为临时表(无索引),在外层SQL需要时调
            用临时表数据进行匹配,这是效率非常低的执行方式,所以
            尽量避免在SQL中使用子查询。
MySQL 优化器统计信息

Ø    MySQL采集的统计信息:每一索引列的基数,
      即索引中每一列中唯一值的个数,由此可以判定
      一个列给它一个值大概可以过滤多少数据
Ø    例如某列基数为1000,表示随机给一个值,通过
      索引可以过滤999/1000的数据
Ø    show index from table可以看到表上每个列的基
      数
!     +--------+------------+---------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
!     | Table | Non_unique | Key_name       | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
!     +--------+------------+---------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
!     | orders |          0 | PRIMARY       |            1 | o_orderkey | A          |     1493508 |     NULL | NULL   |      | BTREE      |         |
!     | orders |          1 | orders_fk1    |            1 | o_custkey   | A         |      213358 |     NULL | NULL   |      | BTREE      |         |
!     | orders |          1 | orders_dt_idx |            1 | o_orderdate | A         |        4567 |     NULL | NULL   |      | BTREE      |         |
!     +--------+------------+---------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
MySQL 优化器判定算法

Ø    MySQL利用索引的原则:尽可能用主键索引>尽
      可能筛去更多的数据>尽可能使用⻓长度小的索引>
      尽可能使用更多索引列。
!     idx_Col1索引⻓长度368,过滤基数9000
!     idx_Col3索引⻓长度128,过滤基数3000
!     idx_Col1_Col2索引⻓长度768,过滤基数11000
!     idx_Col2_Col3索引⻓长度255,过滤基数 9000

!     SELECT Col1, Col2, Col3 FROM t WHERE col1 = x AND col2 = y AND col3
      = z;
!     优先使用idx_Col2_Col3索引,因为过滤基数差不多,索引⻓长度小得多。

!     SELECT Col1, Col2, Col3 FROM t WHERE col1 = x AND col3 = z;
!     优先使用idx_Col1索引,因为col1过滤基数远大于col3,而idx_Col1_Col2因
      为索引⻓长度⻓长的多过滤基数却相近则不被使用
Next Topic




索引设计杂谈
如何平衡查询性能和修改代价

Ø    索引对修改数据的影响有多大?
Ø    为每个列建单列索引?
Ø    字符串类型怎么建索引 算?
Ø    怎么利用好前缀索引?
Ø    JOIN还有排序怎么办?
Ø    JOIN还有WHERE条件怎么处理?
Ø    子查询怎么建索引?
Ø    数据 移该怎么处理索引?

Ø    Any Questions?
Next Topic




编写高性能SQL
编写高性能SQL

Ø    不写不必要的条件(索引判定缺陷)
Ø    只选择必要的列(覆盖索引优化)
Ø     联列类型相同(类型转换速度)
Ø    计数全部用count(*)(优化器特性)
Ø     少子查询,优化为JOIN(优化器缺陷)
Ø    尽量不要使用主键排序,除非有主键作为条件
      (优化器缺陷)
Ø    尽量将 杂查询拆分为简单查询(优化器缺陷)
Ø    尽量将OR查询拆分为多个SQL做UNION ALL或
      在程序拼接(优化器缺陷)
Ø    批量提交修改( 少日志刷新)
Next Topic




设计高性能Schema
设计高性能Schema

Ø    主键必须是顺序插入(索引组织表特性)
Ø    可以用空间换时间多建索引(变更缓冲优化)
Ø    尽量少用唯一键(变更缓冲局限)
Ø    索引可以在SQL需要的列不多的情况下包含不在
      条件中的列(覆盖索引)
Ø    尽量不要设计超过768字节需要作为查询条件的
      列(索引限制)
Ø    不要设计超过4K行⻓长的表(⻚页结构限制)
Next Topic




备份与    策略
可选方案

Ø    cp
      l    直接拷⻉贝文件,只适合停机备份
      l    InnoDB必须拷⻉贝日志才能保证
Ø    mysqldump
      l    MyISAM无法保证数据一致,必须锁定写入
      l    InnoDB可以使用—single-transation
Ø    xtrabackup
      l    在线备份物理文件,导出速度快,可以增量
      l      前需要预处理,  时临时预处理时间⻓长
      l    可以在备份完成后立刻预处理,提升  速度
Ø    replication
      l    搭建延时Slave,可按需导出需要的数据
      l    大集群如何优化维护难题
Next Topic




数据拆分方案
可选方案

Ø    水平拆分
      l    相同拆分键的数据在一起
      l    同一分区键的数据可以JOIN
Ø    垂直拆分
      l    可以不分库
      l    功能之间调用只能使用程序接口
Ø    中间件
      l    自动分库,对应用透明
      l    扩展不 活
      l     发难度较大,不适合中小企业
Ø    程序分库[推荐]
      l    可自定义分库规则
      l     活扩展
      l    无需中间件,直连数据库
Next Topic




  制设计方案
可选方案

Ø    binlog replication
      l    推荐ROW格式,绕过Parse操作
      l    原生可靠
      l    只能1对N模式
Ø    trigger
      l    用触发器记录需要的表
      l    不用全库 制,容易过滤
      l    可以实现任意多主库向任意多备库同步
Ø    中间件
      l    从可配置主库读取binlog,过滤需要的日志部分,传输给需要
            的目标库
      l     活性 强
      l    ROW格式binlog处理速度快
      l     发成本较高
Next Topic




典型故障案例分析
典型故障

Ø    不合理的Schema设计
Ø    不合理的热点数据估算
Ø    不合理的任务调度
Ø    不合理的操作

对MySQL应用的一些总结

  • 1.
    MySQL Optimization MySQL架构设计与优化最佳实践 彭立勋 http://www.PengLiXun.com/ PengLiXun@gmail.com
  • 2.
    Topics of Discussion(上午) l  MySQL缺陷与适用场景 l  MyISAM vs InnoDB l  MyISAM与InnoDB性能调优 l  MySQL Server性能调优 l  MySQL Join算法 l  MySQL 优化器特性 l  索引设计杂谈
  • 3.
    Topics of Discussion(下午) l  编写高性能SQL l  设计高性能Schema l  数据备份与 策略 l  数据拆分方案 l  制设计方案 l  典型故障案例分析 l  构建高性能高可用可扩展MySQL集群
  • 4.
  • 5.
  • 6.
    MySQL缺陷 Ø  MySQL没有Hash Join à 不适合无索引大数据 量数据分析 Ø  MySQL没有完整的CBO à 不可依赖MySQL选 择最优的执行计 ,不可依赖MySQL自行优化 SQL Ø  MySQL没有备块级 制 à 不可依赖MySQL保证 备库的实时一致性 Ø  MySQL无法有效利用主机资源 à 单机单实例会 严重的浪费机器性能
  • 7.
    MySQL适用场景 ü  应用会产生庞大连接数(Thread) ü  可以依赖索引完成的数据分析(Covering Index) ü  高并发查询(MyISAM) ü  短小的在线事务处理(InnoDB) ü  简单条件查询(HandlerSocket) ü  数据拆分(Free)
  • 8.
  • 9.
    MyISAM的优势和劣势 优势 劣势 小内存下更快速的查询:   数据文件容易损坏:   因为没有复杂的MVCC多版本控 因为数据回写机制没有原子性 制机制 保证 更小的数据文件:   没有事务保证   因为数据文件中没有额外的行 锁和事务信息 非常容易冷备份:   只有索引缓存 因为每张表三个文件,没有额 外的全局信息,拷贝文件即可 备份 表级锁,并发差
  • 10.
    InnoDB的优势和劣势 优势 劣势 行级锁,并发高:   小内存下纯读应用不如MyISAM 只锁定被访问到的行,并不影 快 响对不同行的操作。 ACID事务保证 备份操作更复杂:   因为存在事务信息,多版本信 息,不能简单的拷贝数据文件。 但是支持热备份。 所有数据文件均可缓存:   更占用磁盘空间:   InnoDB主键索引即数据,缓存 因为行结构中包含事务信息和 索引就包括了缓存数据 行锁信息,另外有UNDO和REDO 事务日志 读写互不阻塞:   得益于MVCC多版本控制,读数 据并不需要锁定修改,可以在 UNDO日志中找到旧版本。
  • 11.
    索引对比 MyISAM InnoDB B-­‐Tree结构,不能自动Balance B+Tree结构,自动Balance 堆表:   索引组织表:   数据文件独立于索引,数据文 数据文件以主键顺序组织,主 件以插入顺序组织,以物理位 键即数据,非主键索引通过主 置为引用键,索引通过物理位 键查找数据行。所以主键插入 置查找数据行。 无序时性能极差。 支持自适应哈希:   当InnoDB发现表上经常存在某 些唯一键上的KV查询时,会自 动把这部分数据生成到自适应 哈希表中,可进行KV查询优化。 主键索引包含事务信息:   主键索引上唯一确定了一行数 据,所以事务信息包含在主键 索引上。如果没有显示定义主 键索引,将自动建立隐含主键。
  • 12.
    MyISAM场景 ü  数据仓库:大量读,批量写,可共享数据文件 ü  日志系统:只有插入没有修改,简单查询 ü  读为主的应用:没有写或写 少,几乎不阻塞读 ü  低并发的应用:表锁基本不影响并发 ü  大数据批量导入:避免InnoDB索引组织表主键 乱序时插入慢
  • 13.
    InnoDB场景 ü  高并发的应用:例如消息队列 ü  可靠性要求高的应用:例如用户登陆信息 ü  数据一致性要求高,宕机可 :例如用户数据 ü  要求事务和约束的应用:例如有数据 联的系统 ü  需要高性能缓冲写的应用:例如短时间内大量 UPDATE
  • 14.
    同时使用MyISAM / InnoDB Ø  不可共享缓存空间 Ø  SQL包含 表将失去事务意义 总之不推荐这 表同时存在同一实例,除非: 表没有 联,MyISAM表只做备份冗余。 但是,Master用InnoDB,Slave用MyISAM某些情 况下是不错的方案。
  • 15.
  • 16.
    MyISAM优化 l  INSERT 优化 l  Key Buffer 优化
  • 17.
    MyISAM 插入 Ø  定⻓长表优于变⻓长表,即尽可能不定义变⻓长字段 Ø  数据存储按插入顺序,除非删除了数据产生的空 洞可以插入新行 或 使用了并发插入 Ø  可以加大bulk_insert_buffer_size变量,采用 ”INSERT INTO table VALUES (),(),...” 的批量插 入语法,或者采用LOAD DATA语法
  • 18.
    MyISAM 并发插入 虽然MyISAM是表锁,但是如果只在数据文件末尾 插入数据,是可以并发的,通过concurrent_insert 变量控制: n  0 – (Off):不允许并发插入 n  1 – (Default):如果数据文件没有空洞,可以在数据文件末尾并发 插入 n  2 – Enable:如果表在被使用则在数据文件末尾并发插入,如果 表没有被使用则一样在文件中找空洞插入 [建议]
  • 19.
  • 20.
    Key Buffer 介绍 作用:缓存MyISAM的索引,默认16M 相 变量:key_buffer_size(Key Buffer大小) 相 状态量: n  key_read/write_requests(索引逻辑读/写) n  key_reads/writes(索引物理读/写,即磁盘读/写) n  key_blocks_used/unused(已使用/未使用的缓冲块) n  key_blocks_not_flushed(未回写的脏缓冲块)
  • 21.
    Key Buffer 监控 Ø  命中率 = (requests - reads) / requests Ø  利用率 = key_blocks_used / (key_blocks_used + key_blocks_unused) Ø  赃⻚页率 = key_blocks_not_flushed / (key_blocks_used + key_blocks_unused)
  • 22.
    Key Buffer 注意 key_buffer_size不能闭,因为至少mysql系统库 全部用的MyISAM表,就算整个实例都是InnoDB 表,也应该设置key_buffer_size。
  • 23.
    多段 Key Buffer 少Key Buffer锁的竞争: 将Key Buffer拆分为多个段,分 缓冲不同的表。 !  root@localhost : (none) 04:16:36> SET GLOBAL hot_cache.key_buffer_size=128*1024; !  Query OK, 0 rows affected (0.00 sec) !  root@localhost : mysql 04:28:05> CACHE INDEX user IN hot_cache; !  +------------+--------------------+----------+----------+ !  | Table | Op | Msg_type | Msg_text | !  +------------+--------------------+----------+----------+ !  | mysql.user | assign_to_keycache | status | OK | !  +------------+--------------------+----------+----------+ !  1 row in set (0.00 sec)
  • 24.
    预载入 Key Buffer 少Key Buffer预热时间: 预先载入指定表的索引到指定缓冲段。 !  root@localhost : mysql 04:28:13> LOAD INDEX INTO CACHE user; !  +------------+--------------+----------+----------+ !  | Table | Op | Msg_type | Msg_text | !  +------------+--------------+----------+----------+ !  | mysql.user | preload_keys | status | OK | !  +------------+--------------+----------+----------+ !  1 row in set (0.00 sec)
  • 25.
    Key Buffer 建议 最好可以保证KeyBuffer Size大于目前所有需要访 问的表MYI文件大小总和。 如无法保证则一般分三个区给不同业务类型的表: n  静态缓冲区, 少或不修改的表,这个区的Key Buffer访问没有 写锁,都是Shared Read Lock。 n  修改缓冲区,大量修改或UPDATE的表,这个区的Key Buffer竞 争严重,限制竞争在一个区。 n  普通缓冲区,除大量修改和只读表以外的正常读写比例的表。
  • 26.
    刷新 Key Buffer 内存中缓存的索引块,有时候并不会及时刷新到磁盘上,所以对于 正在运行的数据表的索引文件(MYI)一般都是不完整的。如果此时 拷⻉贝或者移动这些索引文件。多半会出现索引文件损坏的情况。可 以通过Flushtable命令来将Key Buffer中的block都flush到磁盘上。 !  mysql> flush tables; !  +------------------------+----------+ !  | Variable_name | Value | !  +------------------------+----------+ !  | Key_blocks_not_flushed | 0 | #所有修改的block都已经被flush了 !  | Key_blocks_unused | 0 | !  | Key_blocks_used | 14497 | !  | Key_read_requests | 38333936 | !  | Key_reads | 207 | !  | Key_write_requests | 8819898 | !  | Key_writes | 1255245 | !  +------------------------+----------+
  • 27.
    InnoDB优化 l  Buffer Pool 调优 l  日志 调优 l  XtraDB调优
  • 28.
    InnoDB 简介 Ø  InnoDB是MySQL上应用最广的事务引擎,淘宝 商品库、阿里巴巴商品信息全部 在InnoDB 上。 Ø  InnoDB目前包含 个版本,InnoBase和InnoDB Plugin,5.1之前只有InnoBase,5.1之后 始有 InnoDB Plugin。另外还有基于MySQL Server和 InnoDB Plugin优化的Percona Server和 XtraDB。 Ø  前阿里巴巴正在使用的就是Percona+XtraDB高 性能版本,完全兼容MySQL。
  • 29.
  • 30.
    InnoDB 数据文件组织 Ø  最基本的修改单位是Row Ø  最基本的IO单位是Page,包含2个以上Row Ø  最基本的分配单位是Extent,包含64个Page Ø  InnoDB的索引都是指到Page no为止,为了B- Tree保持高效,InnoDB要求一个Page内至少有 2行数据,所以我们建议每行数据设计⻓长度不要 超过4K,这样我们可以方便的 移到最平衡的 8K⻚页大小。
  • 31.
  • 32.
  • 33.
  • 34.
    Buffer Pool InnoDB最重要的一块内存就是Buffer Pool,它包含 了几乎所有InnoDB数据和索引的缓冲,主要有: n  数据缓存(主键缓存) n  索引缓存(非主键索引缓存) n  自适应哈希 n  数据字典 n  ⻚页哈希等
  • 35.
    Buffer Pool 设置 innodb_buffer_pool_size设置BufferPool的大小。 建议将除了连接使用的内存,操作系统使用的内存 之外的全部内存给Buffer Pool,不要靠操作系统的 OS Cache来缓冲缓存数据。 经验参考值: n  48G内存:32G Buffer Pool n  32G内存:20G Buffer Pool n  24G内存:16G Buffer Pool n  16G内存:10G Buffer Pool
  • 36.
    Buffer Pool 赃⻚页比例 BufferPool同时担任缓冲和缓存工作,因此Buffer Pool中会产生赃⻚页,即在内存中修改了但未刷新到 磁盘上的⻚页。 innodb_max_dirty_pages_pct变量可以配置最多允 许多少比例的赃⻚页在内存中,超过比例将触发刷 新。 经验参考值: n  75%适用于绝大部分情况,Google/Facebook/Alibaba均使用 n  5%适用于非常在意宕机快速 的情况,Taobao/Alipay使用
  • 37.
    InnoDB 事务安全 Ø  REDO LOG:所有变更内容全部写入REDO LOG。一 旦数据库Crash,将redo中已经标记为commit状态的事 务全部重做到数据文件,将没有标记为commit的事务在 数据文件中回 。 Ø  UNDO LOG:所有数据被修改的旧版本数据和版本号 写入undo,当需要回 事务时,在undo中找到旧版本 信息,覆盖数据文件。 Ø  Double Write Buffer:所有的⻚页级回写,都先将⻚页写 入Double Write Buffer区域, 认CheckSum与内存中 的⻚页一致,再正式写入数据文件中,如果数据库Crash, 数据文件中的⻚页没有回写完成,可以从Double Write Buffer中读取正 的⻚页重写。
  • 38.
  • 39.
    Log Buffer Ø  事务安全使得InnoDB会产生事务日志,包括 REDO和UNDO。日志回写到磁盘会有一个Log Buffer缓冲日志写 Ø  innodb_log_buffer_size设置事务日志缓冲大小 Ø  经验参考值: n  默认只有1M,存在大事务或者高并发情况,建议设置8~16M
  • 40.
    InnoDB 事务提交 Ø  Log Buffer中缓冲的日志在检查点或事务提交时 回写文件。 Ø  Buffer Pool中的⻚页在检查点时回写磁盘。 Ø  innodb_flush_log_at_trx_commit控制回写: n  0: 每秒刷新日志到文件(不是磁盘) n  1: 每次事务提交刷新日志到磁盘 [default] n  2: 每次事务提交刷新日志到文件,每秒同步到磁盘 [建议]
  • 41.
    日志大小设置 Ø  更大的事务日志意味着什么? n  可以记录更大的事务信息 [可以重做更大的事务] n  可以记录更⻓长的事务历史 [可以重做更多的事务] n  更少的检查点 [ 少磁盘IO] n  更⻓长的 时间 [宕机重启时间更⻓长] Ø  经验参考值: n  64M的日志5分钟以内就可以 n  1G的日志全部重做需要30分钟以上 n  我们设置为3个1G的日志
  • 42.
    InnoDB 锁机制 Ø  InnoDB锁算法:写数据有索引时采用间 锁(Gap Lock),无索引时采取表锁。读利用MVCC机制,不加 锁。所以DELETE / UPDATE的条件没有加索引后果很 严重。 n  WHERE pk = 1,锁定pk=1的行,不可以修改pk=1的数据。 n  WHERE pk > 1,锁定pk>1后的所有行,不可以操作pk>1的 数据,包括插入删除修改。 n  WHERE pk BETWEEN 1 AND 10,锁定1~10内所有行,不 可以操作pk在1~10的所有数据。 Ø  InnoDB锁结构:通过为每个⻚页生成一个锁定位图,判 定行是否被锁定,每⻚页的第一行和最后一行是系统行, 如果被锁定说明整⻚页被锁。
  • 43.
    InnoDB MVCC Ø  多版本实现: !  数据旧版本写入UNDO LOG,数据Page中保留最新数据和版本 号,当Query发现Page中的数据版本太新,则顺着undo log pointor到UNDO LOG寻找到需要的历史版本,所以读取数据不 需要加锁。 Ø  事务可⻅见性判定: !  MVCC需要做版本可⻅见性判定,以 认一个版本号的数据是否可 以为当前事务所⻅见。InnoDB使用read view结构来实现版本可⻅见, 一个事务起始时,会被分配一个read view,包含当前正在执行的 事务号,根据不同的隔离级 需求,可以依据read view的事物号 信息来判定行数据可⻅见性。 !  例如MySQL默认隔离级 为可重 读(Repeatable Read),那 么只要读取到的数据事务号大于read view中的最小事务号,就是 不可⻅见的。
  • 44.
    自适应哈希 Ø  自适应哈希是InnoDB的天才设计之一,InnoDB存储引 擎会智能的监控对表上索引的查找,如果观察到建立哈 希索引可以带来速度的提升,则自动建立哈希索引。 Ø  自适应哈希索引通过缓冲池的B+树构造而来,因此建立 的速度很快。并不将整个表都建哈希索引,InnoDB存 储引擎会自动根据访问的频率和模式来为某些⻚页建立哈 希索引。 !  ------------------------------------- !  INSERT BUFFER AND ADAPTIVE HASH INDEX !  ------------------------------------- !  Ibuf: size 2249, free list len 3346, seg size 5596, !  374650 inserts, 51897 merged recs, 14300 merges !  Hash table size 4980499, node heap has 1246 buffer(s) !  1640.60 hash searches/s, 3709.46 non-hash searches/s
  • 45.
    Insert Buffering Ø  Insert Buffer是InnoDB的另一个天才设计,当有数据更 新时,不立刻更新Index叶子节点的Page,而是将这些 更新缓冲起来,因此对同一个Page的更新可以一起回 写,这 将离散IO整合为顺序IO的设计,非常适合现在 的SSD盘。 Ø  但是Insert Buffer只对非唯一索引起作用,因为在做 Merge之前检查值是否唯一是不可能的。 !  ------------------------------------- !  INSERT BUFFER AND ADAPTIVE HASH INDEX !  ------------------------------------- !  Ibuf: size 7545, free list len 3790, seg size 11336, !  8075308 inserts, 7540969 merged recs, 2246304 merges
  • 46.
    InnoDB 优化实践 ü  尽可能使用小的主键: 少非主键索引大小 ü  按主键顺序插入数据:避免主键节点分裂 ü  加大日志大小:避免过多的检查点 ü  避免大事务回 :会产生大量的随机IO,拆分事 务 ü  避免零散的插入:尽可能批量插入 ü  尽量 用前缀索引:InnoDB不会压缩索引
  • 47.
    InnoDB 其他重要变量 Ø  innodb_additional_mem_pool_size:内部元信 息缓存,16M足以胜任任何情况 Ø  innodb_support_xa:是否支持 段提交协议, 没有必要 启, 闭可获得10%的性能提升。
  • 48.
    InnoDB Plugin 增强 Ø  IO容量:innodb_io_capacity Ø  IO线程数: n  innodb_read_io_threads(预读) n  innodb_write_io_threads(赃⻚页回写) n  innodb_use_purge_thread(清理UNDO) Ø  可变⻚页大小(innodb_page_size) Ø  可配置多回 段(innodb_extra_rsegments) Ø  Compressed表结构:CPU换IO,压缩空间 Ø  扩展INFORMATION_SCHEMA
  • 49.
    InnoDB Plugin 配置 Ø  建议经验值: n  innodb_io_capacity = 磁盘数*200 (SSD>4000) n  innodb_read_io_threads = 4 n  innodb_write_io_threads = CPU核数/实例数*2 n  innodb_use_purge_thread = 回 段/2 n  innodb_page_size = 8K (全表扫描16K,小数据4K) n  innodb_extra_rsegments = 平均并发插入数,但不要超过 CPU数*2 n  字符型列为主的表设置Compressed表结构
  • 50.
  • 51.
    Table Cache 介绍 TableCache是表高速缓存,缓存表文件句柄,每 张表被访问前线程都需要获得它的文件句柄,如果 在表缓存中命中,则不需要再次打 文件,直接从 缓存中获取文件句柄。 !  root@localhost : (none) 12:36:11> show global status like 'open%tabl%'; !  +--------------------------+-------+ !  | Variable_name | Value | !  +--------------------------+-------+ !  | Open_table_definitions | 18 | !  | Open_tables | 12 | # 当前打 的表数量 !  | Opened_table_definitions | 24 | !  | Opened_tables | 24 | # 表曾经被打 多少次 !  +--------------------------+-------+
  • 52.
    Table Cache 调优 建议经验值: n  一般table_open_cache=1024适用于大部分情况。 n  如果opened_tables不断增⻓长,应调大table_open_cache。 查看目前打 的表: !  root@localhost : (none) 12:39:24> show open tables; !  +----------+--------------+--------+-------------+ !  | Database | Table | In_use | Name_locked | !  +----------+--------------+--------+-------------+ !  | mysql | servers | 0 | 0 | !  | mysql | event | 0 | 0 | !  | test | t3 | 0 | 0 | !  | mysql | procs_priv | 0 | 0 | !  | mysql | db | 0 | 0 | !  | mysql | host | 0 | 0 | !  | test | t2 | 0 | 0 | !  +----------+--------------+--------+-------------+
  • 53.
    Query Cache 介绍 QueryCache是独立于存储引擎的SQL级缓存,它 实际上是SQL文本的MD5值和SQL执行结果的哈希 键对。因此Query Cache的使用受到很大限制: n  SQL文本必须完全相同,包括大小写,空格 n  存在不 定函数的SQL不被缓存,例如NOW() n  PREPARE方式执行的SQL不被缓存 n  自定义函数,存储过程,视图均不能被缓存
  • 54.
    Query Cache 设置 Ø  配置参数: n  query_cache_type: n  OFF:不对SELECT结果集缓存 n  ON:对SELECT结果集缓存 [默认] n  DEMAND:只有当SELECT语句中显示写明“SQL_CACHE”时候才对 结果集缓存 [推荐] n  query_cache_size:Query Cache大小 n  query_cache_limit:可缓存的最大结果集,默认1MB n  query_cache_min_res_unit:内存分配最小单位,默认4KB Ø  经验参考值: n  InnoDB应该 闭QCache,因为InnoDB有MVCC n  MyISAM如果 实数据很少变化,可以测试常用SQL的结果集 大小,设置为最常用的SQL的结果集
  • 55.
    Query Cache 监控 Ø  命中率 = Qcache_hits / (Com_select + Qcache_hits) Ø  利用率 = (query_cache_size - Qcache_free_memory) / query_cache_size Ø  碎片率 = Qcache_free_blocks / Qcache_total_blocks !  mysql> show status like ‘Qcache%’; !  +-------------------------+----------+ !  | Variable_name | Value | !  +-------------------------+----------+ !  | Qcache_free_blocks | 1 | # 缓存中有多少未被使用空闲的内存块 !  | Qcache_free_memory | 16768400 | # 可用的缓存空间 !  | Qcache_hits | 0 | # 缓存命中的次数 !  | Qcache_inserts | 0 | # 被缓存的查询次数,也就是没有命中的次数 !  | Qcache_lowmem_prunes | 0 | # 由于内存不足导致被删除的缓存条目数量 !  | Qcache_not_cached | 64 | # 无法被缓存的查询的数量 !  | Qcache_queries_in_cache | 0 | # 当前被cache的查询数量 !  | Qcache_total_blocks | 1 | # 当前使用的内存块的数量 !  +-------------------------+----------+
  • 56.
    Query Cache 优化 Ø  如果Qcache_lowmem_prunes很大,说明Query Cache大小不足 Ø  如果命中率很低,说明Query Cache没有必要 Ø  如果Qcache_free_blocks很大,说明Query Cache需要整理碎片,Flush Query Cache。 Ø  query_cache_min_res_unit = (query_cache_size - Qcache_free_memory) / Qcache_queries_in_cache
  • 57.
    Thread Cache 介绍 每个连接到MySQL都需要创建一个线程,Thread Cache就缓存了已经断的连接线程描述符,如果 创建线程前检测到Thread Cache还有可用线程, 就不需要新创建,从缓存中获取即可。 !  void create_thread_to_handle_connection(THD *thd) { !  if (cached_thread_count > wake_thread) { //看线程缓存(thread_cache)中有否空 余的线程 !  thread_cache.append(thd); !  pthread_cond_signal(&COND_thread_cache); // 有的话则唤醒一个线程来用 !  } else { !  threads.append(thd); !  pthread_create(&thd->real_id,&connection_attrib, handle_one_connection, (void*) thd))); //没有可用空闲线程则创建一个新的线程 !  } !  }
  • 58.
    Thread Cache 配置 Ø  经验建议值: n  thread_cache_size = 1024 适应绝大部分场景 n  并发连接数很少时,相应降低配置 Ø  优化建议: n  如果threads_created增⻓长很快,应该调大thread_cache_size 的值
  • 59.
    连接数 Ø  配置变量: n  max_connections限制全局连接数 n  max_user_connections限制每个用户的连接数 Ø  建议经验值: n  max_user_connections:没有特殊情况不用限制 n  max_connections:默认100。一般1000个连接可以满足绝大 部分应用,但是应注意,每个连接都会申请线程级内存(包 括Sort Buffer,Join Buffer,Binlog Cache等),要考虑内存 是否可以承受。 n  我们设置为3000,因为很少有连接需要排序和JOIN,一般不 需要多少线程级内存。
  • 60.
    Sort Buffer 介绍 Ø  每个线程需要排序的时候都会申请一块Sort Buffer,线程第一次使用的时候分配,连接断 后线程不销毁则内存不释放。 Ø  如果要排序的数据大于Sort Buffer将会用归并排 序法分块排序,超出内存部分写入磁盘暂存。 Ø  sort_buffer_size 根据需要排序的数据集大小而 定,可以按照Session在连接内重新设定。
  • 61.
    Sort Buffer 监控 Ø  Sort_merge_passes:归并排序次数,这个值很大说明 Sort Buffer不 ,很多排序数据写到磁盘上了 Ø  Sort_rows:被排序数据的行数 Ø  Sort_range:通过索引选择了一个范围的数据后排序 Ø  Sort_scan:通过全表扫描进行排序的次数,这个值很 大应该优化索引避免排序
  • 62.
    Join / ReadBuffer Ø  join_buffer_size:JOIN时如果不能用到索引就 会使用Join Buffer进行JOIN,最好优化索引避免 使用Join Buffer。 Ø  read_buffer_size:全表扫描时缓存读取的数 据,最好通过索引避免全表扫描。
  • 63.
    Tmp Table 介绍 当执行计中有Using temporary时表示要采用临时 表完成SQL。 tmp_table_size配置在内存中分配临时表的最大限 制,超过部分将建到磁盘上。 !  root@localhost : test 04:21:53> explain select * from t1,t2 where t1.info=t2.info order by t1.id; !  +----+-------------+-------+------+---------------+------+---------+-------------- +------+---------------------------------+ !  | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | !  +----+-------------+-------+------+---------------+------+---------+-------------- +------+---------------------------------+ !  | 1 | SIMPLE | t2 | ALL | NULL | NULL | NULL | NULL | 2 | Using temporary; Using filesort | !  | 1 | SIMPLE | t1 | ref | idx | idx | 32 | test.t2.info | 1 | Using index | !  +----+-------------+-------+------+---------------+------+---------+-------------- +------+---------------------------------+
  • 64.
    Tmp Table 监控 Ø  重要状态: n  Created_tmp_disk_tables: 件磁盘临时表数量 n  Created_tmp_tables:创建临时表数据 Ø  建议经验值: n  一般tmp_table_size设置为64M足以应对大部分情况 n  如果Created_tmp_tables增⻓长很快,最好优化SQL和索引避 免临时表 n  如果Created_tmp_disk_tables增⻓长很快,应调大 tmp_table_size的值
  • 65.
  • 66.
    JOIN 算法概述 Ø  MySQL只有一 JOIN算法:Nested Loop Join Ø  Nested Loop Join:选择一张驱动表,用 联字 段循环匹配被驱动表 !  FOR EACH ROW row1 IN table1 { !  FOR EACH ROW row2 IN table2 IF( 联条件) { !  result := row1 || row2; !  OUTPUT result; !  } } Ø  支持条件下推(Push Down):先用条件过滤驱动 表和被驱动表,再把过滤后的结果集进行JOIN
  • 67.
    内连接 Ø  假定我们有一个如下形式的表T1、T2、T3的联 接查询:SELECT * FROM t1 INNER JOIN t2 ON c1(t1,t2) INNER JOIN t3 ON c2 (t2,t3) WHERE c(t1,t2,t3) Ø  MySQL首先由优化器判定JOIN顺序,假设生成 执行计 为t1驱动t2驱动t3: !  FOR EACH ROW row1IN t1 IF (c(t1)){ !  FOR EACH ROW row2 IN t2 IF (c1(t1,t2) && c(t2)) { !  FOR EACH ROW row3 IN t3 IF (c2(t2,t3) && c(t3)) { !  result := row1 || row2 || row3; !  OUTPUT result; !  } } } Ø  将行传给下一层被驱动表匹配时,先用条件进行 过滤, 少参与JOIN的行数
  • 68.
    外连接 Ø  SQL同前例: !  FOR EACH ROW row1 IN t1 IF c1(t1) { !  BOOL f1 := FALSE; !  FOR EACH ROW row2 IN t2 IF c1(t1,t2) AND (f1 ? c2(t2) : TRUE) { !  BOOL f2 := FALSE; !  FOR EACH ROW row3 IN t3 IF c2(t2,t3) AND (f1&&f2 ? c3(t3) : TRUE) { !  IF (f1&&f2 ? TRUE : (c2(t2) AND c3(t3))) { !  result := row1 || row2 || row3; OUTPUT result; } !  f2 := TRUE; f1 := TRUE; } !  IF (!f2) { !  IF (f1 ? TRUE : c2(t2) && c(t1,t2,NULL)) { !  result := row1 || row2 || NULL; OUTPUT t; } !  f1 := TRUE; } } !  IF (!f1 && c(t1,NULL,NULL)) { !  result := row1||NULL||NULL; OUTPUT t; } } Ø  经过测试,三张10000行的表用外连接平均比内 连接慢0.01s,内连接效率高于外连接。
  • 69.
  • 70.
    MySQL 优化器概述 Ø  MySQL优化器属于RBO(基于规则的优化器), 5.1版本后带有少量的CBO(基于 销的优化器) 特性。 Ø  MySQL优化器缺陷: n  不支持函数索引:WHERE func(col) = x 无法使用col字段上 的索引 n  不支持索引过滤 (Index Filter) 但支持索引覆盖扫描: n  SELECT col1,col2,col3,col4 FROM t WHERE col1 > x AND col2 < y AND col3 >z; 无法利用 (col1,col2,col3)索引过滤col2和col3,需要回表查询。 n  SELECT col1,col2,col3 FROM t WHERE col1 > x AND col2 < y AND col3 >z; 可以利用 (col1,col2,col3)索引扫描到col1,col2,col3的值不用回表。 n  无法自动优化子查询:MySQL无法将子查询优化为JOIN方式, 而是将子查询存为临时表(无索引),在外层SQL需要时调 用临时表数据进行匹配,这是效率非常低的执行方式,所以 尽量避免在SQL中使用子查询。
  • 71.
    MySQL 优化器统计信息 Ø  MySQL采集的统计信息:每一索引列的基数, 即索引中每一列中唯一值的个数,由此可以判定 一个列给它一个值大概可以过滤多少数据 Ø  例如某列基数为1000,表示随机给一个值,通过 索引可以过滤999/1000的数据 Ø  show index from table可以看到表上每个列的基 数 !  +--------+------------+---------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ !  | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | !  +--------+------------+---------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ !  | orders | 0 | PRIMARY | 1 | o_orderkey | A | 1493508 | NULL | NULL | | BTREE | | !  | orders | 1 | orders_fk1 | 1 | o_custkey | A | 213358 | NULL | NULL | | BTREE | | !  | orders | 1 | orders_dt_idx | 1 | o_orderdate | A | 4567 | NULL | NULL | | BTREE | | !  +--------+------------+---------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
  • 72.
    MySQL 优化器判定算法 Ø  MySQL利用索引的原则:尽可能用主键索引>尽 可能筛去更多的数据>尽可能使用⻓长度小的索引> 尽可能使用更多索引列。 !  idx_Col1索引⻓长度368,过滤基数9000 !  idx_Col3索引⻓长度128,过滤基数3000 !  idx_Col1_Col2索引⻓长度768,过滤基数11000 !  idx_Col2_Col3索引⻓长度255,过滤基数 9000 !  SELECT Col1, Col2, Col3 FROM t WHERE col1 = x AND col2 = y AND col3 = z; !  优先使用idx_Col2_Col3索引,因为过滤基数差不多,索引⻓长度小得多。 !  SELECT Col1, Col2, Col3 FROM t WHERE col1 = x AND col3 = z; !  优先使用idx_Col1索引,因为col1过滤基数远大于col3,而idx_Col1_Col2因 为索引⻓长度⻓长的多过滤基数却相近则不被使用
  • 73.
  • 74.
    如何平衡查询性能和修改代价 Ø  索引对修改数据的影响有多大? Ø  为每个列建单列索引? Ø  字符串类型怎么建索引 算? Ø  怎么利用好前缀索引? Ø  JOIN还有排序怎么办? Ø  JOIN还有WHERE条件怎么处理? Ø  子查询怎么建索引? Ø  数据 移该怎么处理索引? Ø  Any Questions?
  • 75.
  • 76.
    编写高性能SQL Ø  不写不必要的条件(索引判定缺陷) Ø  只选择必要的列(覆盖索引优化) Ø  联列类型相同(类型转换速度) Ø  计数全部用count(*)(优化器特性) Ø  少子查询,优化为JOIN(优化器缺陷) Ø  尽量不要使用主键排序,除非有主键作为条件 (优化器缺陷) Ø  尽量将 杂查询拆分为简单查询(优化器缺陷) Ø  尽量将OR查询拆分为多个SQL做UNION ALL或 在程序拼接(优化器缺陷) Ø  批量提交修改( 少日志刷新)
  • 77.
  • 78.
    设计高性能Schema Ø  主键必须是顺序插入(索引组织表特性) Ø  可以用空间换时间多建索引(变更缓冲优化) Ø  尽量少用唯一键(变更缓冲局限) Ø  索引可以在SQL需要的列不多的情况下包含不在 条件中的列(覆盖索引) Ø  尽量不要设计超过768字节需要作为查询条件的 列(索引限制) Ø  不要设计超过4K行⻓长的表(⻚页结构限制)
  • 79.
  • 80.
    可选方案 Ø  cp l  直接拷⻉贝文件,只适合停机备份 l  InnoDB必须拷⻉贝日志才能保证 Ø  mysqldump l  MyISAM无法保证数据一致,必须锁定写入 l  InnoDB可以使用—single-transation Ø  xtrabackup l  在线备份物理文件,导出速度快,可以增量 l  前需要预处理, 时临时预处理时间⻓长 l  可以在备份完成后立刻预处理,提升 速度 Ø  replication l  搭建延时Slave,可按需导出需要的数据 l  大集群如何优化维护难题
  • 81.
  • 82.
    可选方案 Ø  水平拆分 l  相同拆分键的数据在一起 l  同一分区键的数据可以JOIN Ø  垂直拆分 l  可以不分库 l  功能之间调用只能使用程序接口 Ø  中间件 l  自动分库,对应用透明 l  扩展不 活 l  发难度较大,不适合中小企业 Ø  程序分库[推荐] l  可自定义分库规则 l  活扩展 l  无需中间件,直连数据库
  • 83.
    Next Topic 制设计方案
  • 84.
    可选方案 Ø  binlog replication l  推荐ROW格式,绕过Parse操作 l  原生可靠 l  只能1对N模式 Ø  trigger l  用触发器记录需要的表 l  不用全库 制,容易过滤 l  可以实现任意多主库向任意多备库同步 Ø  中间件 l  从可配置主库读取binlog,过滤需要的日志部分,传输给需要 的目标库 l  活性 强 l  ROW格式binlog处理速度快 l  发成本较高
  • 85.
  • 86.
    典型故障 Ø  不合理的Schema设计 Ø  不合理的热点数据估算 Ø  不合理的任务调度 Ø  不合理的操作