zoukankan      html  css  js  c++  java
  • mysql之innodb存储引擎介绍

    一、Innodb体系架构

    1.1、后台线程

      后台任务主要负责刷新内存中的数据,保证缓冲池的数据是最近的数据,此外还将修改的数据刷新到文件磁盘,保证在数据库发生异常的情况下Innodb能恢复到正常的运行状态。

    1、Master Thread
    主要负责缓冲池的数据异步刷新到磁盘,保证数据的一致性,包括脏页的刷新,合并插入缓冲undo页的回收等。

    2、IO Thread
    主要负责IO请求的回调(call back)(有如下四种io线程)
    write IO :innodb_write_io_threads=8
    read IO :innodb_read_io_threads=8
    insert buffer IO
    log IO
    查看:show engine innodb statusG (注意:该命令显示不是当前innodb的状态,而是过去一段时间的状态)

    --------
    I/O thread 0 state: waiting for completed aio requests (insert buffer thread)
    I/O thread 1 state: waiting for completed aio requests (log thread)
    I/O thread 2 state: waiting for completed aio requests (read thread)
    I/O thread 3 state: waiting for completed aio requests (read thread)
    I/O thread 4 state: waiting for completed aio requests (read thread)
    I/O thread 5 state: waiting for completed aio requests (read thread)
    I/O thread 6 state: waiting for completed aio requests (read thread)
    I/O thread 7 state: waiting for completed aio requests (read thread)
    I/O thread 8 state: waiting for completed aio requests (read thread)
    I/O thread 9 state: waiting for completed aio requests (read thread)
    I/O thread 10 state: waiting for completed aio requests (write thread)
    I/O thread 11 state: waiting for completed aio requests (write thread)
    I/O thread 12 state: waiting for completed aio requests (write thread)
    I/O thread 13 state: waiting for completed aio requests (write thread)
    I/O thread 14 state: waiting for completed aio requests (write thread)
    I/O thread 15 state: waiting for completed aio requests (write thread)
    I/O thread 16 state: waiting for completed aio requests (write thread)
    I/O thread 17 state: waiting for completed aio requests (write thread)

          从上可以看到IO thread 0为insert buffer thread,IO  thread 1 为log thread,之后的就是根据innodb_write_io_threads=8,innodb_read_io_threads=8设置的读写线程,并且读线程的ID总是小于写线程的ID。

    3、Purge Thread
    用来回收已经使用并分配的undo页(需要随机读取undo页)
    innodb_purge_threads=2  #值相对大点可提高undo页的回收速度,也能更进一步利用磁盘的随机读写性能

    查看值的设置:show variables like 'innodb_purge_threads'G;

    4、Page Cleaner Thread
    将之前版本脏页的刷新操作都放入单独的线程中完成,减轻master thread的负担和用户查询的阻塞

    1.2、内存

    Innodb内存结构图

     innodb内存数据对象

    1、缓冲池

      innodb基于磁盘存储,由于cpu和磁盘之间的速度差距太大,所以通过内存作为一个缓冲,所以缓冲是为了解决磁盘和cpu的速度上差距的问题,提高数据库性能。将磁盘读到的页存放在缓冲中,称为FIX在缓冲池中,从缓冲中读取数据,称为该页在缓冲池被命中。

    数据读:先从缓冲池中读取,如果页被命中就返回,否则从磁盘读取
    数据写:先修改缓冲池中页的数据,然后以一定的频率(checkpoint机制)刷新到磁盘,不是实时刷新

    缓冲池的大小参数:innodb_buffer_pool_size=32G #该值直接影响数据库的整体性能
    多个缓冲池实例:innodb_buffer_pool_instances=4 #减少数据内部资源竞争。

    sql查看:select POOL_ID,POOL_SIZE,FREE_BUFFERS,DATABASE_PAGES from information_schema.INNODB_BUFFER_POOL_STATS;

    查看:show engine innodb statusG

    INDIVIDUAL BUFFER POOL INFO
    ----------------------
    ---BUFFER POOL 0 #实例1
    Buffer pool size        1548287 #页的个数
    Buffer pool size, bytes 25367134208
    Free buffers            1024 #Free List中页的数量
    Database pages          1502522 #LRU List中页的数量
    Old database pages      375610
    Modified db pages       48677
    Percent of dirty pages(LRU & free pages): 3.237
    Max dirty pages percent: 50.000
    Pending reads 0
    Pending writes: LRU 0, flush list 0, single page 0
    Pages made young 422595788, not young 41434529403 #pages made young显示LRU中移动到前端次数,not young表示没能移动到前端次数
    0.60 youngs/s, 0.00 non-youngs/s
    Pages read 2001192602, created 104217673, written 638270606
    0.60 reads/s, 0.00 creates/s, 3.60 writes/s
    Buffer pool hit rate 999 / 1000, young-making rate 1 / 1000 not 0 / 1000 #缓冲命中率,越高越好,如果低于95%,则该考虑是否有扫描,数据备份等操作污染了LRU List
    Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
    LRU len: 1502522, unzip_LRU len: 0
    I/O sum[1744]:cur[25], unzip sum[0]:cur[0]
    ---BUFFER POOL 1 #实例2
    Buffer pool size        1548287
    Buffer pool size, bytes 25367134208
    Free buffers            1024
    Database pages          1502549
    .........

          从Innodb的内存对象图看,缓冲数据类型:索引页,数据页,undo页,插入缓冲(insert buffer),自适应哈希索引,数据字典信息等。缓冲池不仅仅只有缓存索引页和数据页,它们只是占缓冲池中很大一部分。

    2、LRU List、Free List、Flush List

         数据库中的缓冲池是通过LRU(Lastet Recent Used) 最近很少使用算法进行管理。原理:最频繁使用的页放在LRU列前端,不经常使用的放在LUR的后端。

         innodb引擎中,缓冲池中的页默认大小16k,虽然使用了LRU管理,但是在其中间加入了midpoint点(可以理解为把LRU列分为两段),一段为频繁使用,一段为很少使用,通过参数:innodb_old_blocks_pct=30,(意思为30%)控制比例。

        加midpoint的优点:像扫描类的操作很容易将其他常用的页刷新出去,导致效率下降,midpoint可以解决这个问题。另外innodb_old_blocks_time控制列进去LRU进去热端的时间,这样可以保证热端的页的访问频率比较高。

        LRU列是用来管理已读取到的页,当服务刚启动的时候,LRU为空,页都在Free列中,从Free中读取空闲页到LRU中,并删除Free中的页LRU中页从old到new端称为page made young,因innodb_old_blocks_time而没能从old到new端称为page not made young。查看:show engine innodb statusG(如上代码框中)

        Flush列管理脏页(modified db pages),管理将页刷回磁盘,与LRU不影响,LRU用于管理缓冲池中可用的列

    3、重做日志缓冲

    innodb先将重做日志信息存放在这个缓冲中,然后按照一定的频率将其刷新到重做日志文件中
    参数控制:innodb_log_buffer_size=8M (一般不需要很大,因为每秒会刷缓存到日志文件中)

    重做日志缓冲刷新条件:
    1.master Thread每一秒将缓冲刷入重做日志文件中
    2.每个事务提交会将重做日志缓冲刷入重做日志文件
    3.当重做日志缓冲剩下不到1/2时,会刷入重做日志文件

    4、额外内存池

          在innodb的引擎中,对内存的管理是通过一种为内存堆的方式进行管理,对一些数据的内存分配时,需要从额外的内存池中申请,如果额外内存池资源不够,就会从缓冲池中申请。

    二、checkpoint

         为了防止数据丢失:采用Write Ahead Log策略,提交事务时,先写重做日志,然后再修改页,就算在从缓冲池中刷新到磁盘过程中宕机,也可以从日志中恢复数据,防止数据丢失。

    checkpoint技术特点:
    1.缩短数据库的恢复时间(把日志生成数据到磁盘,直接加载磁盘数据,而不是通过日志重新恢复数据)
    2.缓冲池不够用,将脏页写入磁盘
    3.重做日志不可用,刷新脏页

    checkpoint种类:
    sharp checkpoint:发生在数据库关闭时将所有数据刷回磁盘(默认工作方式,innodb_fast_shutdown=1)
    fuzzy checkpoint:只刷新部分脏页,而不是所有脏页到磁盘

    fuzzy checkpoint发生条件:
    master Thread checkpoint :主线程定时checkpoint
    FLUSH_LRU_LIST checkpoint :LRU列不够存
    Async/Sync Flush checkpoint :重做日志不可用,保证重做日志的循环利用
    Dirty page too much checkpoint :脏页数量过多 innodb_max_dirty_pages_pct

    三、Innodb关键特性

    3.1 插入缓冲

    insert buffer

    聚集索引:也叫聚簇索引。数据行的物理顺序与列值(一般是主键的那一列)的逻辑顺序相同,一个表中只能拥有一个聚集索引。

    非聚集索引(辅助索引):该索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同,一个表中可以拥有多个非聚集索引。

           innodb对于非聚集索引的插入和更新操作,不是每一次插入到索引页中,而是先判断非聚集索引页是否在缓冲池中,如在,则直接插入,若不在,则先放入一个insert buffer中,然后再以一定的频率和情况进行insert buffer 和辅助索引页子节点的合并,这是通常能将多个插入操作合并成一个操作。这就大大提高了非聚集索引的效率

    insert buffer发生必须满足两个条件:
    1、所以必须是辅助索引
    2、索引不是唯一的

    存在的问题:当遇到大量的写入时,插入缓冲会占用太多的缓冲池内存,最大默认为1/2,

    change buffer(插入缓冲升级版)

    这个版本的的插入缓冲可以对DML操作--insert、delete、update进行缓冲,分别是insert buffer, delete buffer , purge buffer

    innodb_change_buffer_max_size #该参数用来控制changer buffer的最大值,默认为25,也就是1/4,最大可为50

    查看:show engine innodb statusG

    -------------------------------------
    INSERT BUFFER AND ADAPTIVE HASH INDEX
    -------------------------------------
    Ibuf: size 1, free list len 948959, seg size 948961, 870033008 merges
    merged operations:
     insert 4536471183, delete mark 24386361779, delete 4019249458
    discarded operations:
     insert 11214, delete mark 0, delete 0
    449.91 hash searches/s, 426.91 non-hash searches/s

           如上包括了merged operation和discarded operation。其中的数字表示次数,insert表示insert buffer,delete mark 表示delete buffer,delete 表示purge buffer。discarded opreation的操作表示merge时删除了,无需再合并到索引中。

    change insert底层实现:B+tree (不详述)

    merge insert buffer发生情况:

    1.辅助索引页被读取到缓冲池时
    2.master thread
    3.辅助索引页无可用空间

    3.2 两次写

          InnoDB的Page Size一般是16KB,其数据校验也是针对这16KB来计算的,将数据写入到磁盘是以Page为单位进行操作的。我们知道,由于文件系统对一次大数据页(例如InnoDB的16KB)大多数情况下不是原子操作,这意味着如果服务器宕机了,可能只做了部分写入。16K的数据,写入4K时,发生了系统断电/os crash ,只有一部分写是成功的,这种情况下就是partial page write问题。

        double write基本思路:就是在写数据页之前,先把这个数据页写到一块独立的物理文件位置(ibdata),然后再写到数据页。这样在宕机重启时,如果出现数据页损坏,那么在应用redo log之前,需要通过该页的副本来还原该页,然后再进行redo log重做。

     double write组成:一部分是InnoDB内存中的double write buffer,大小为2M,另一部分是物理磁盘上ibdata系统表空间中大小为2MB,共128个连续的Page,既2个分区。其中120个用于批量写脏,另外8个用于Single Page Flush。做区分的原因是批量刷脏是后台线程做的,不影响前台线程。而Single page flush是用户线程发起的,需要尽快的刷脏并替换出一个空闲页出来。

        double write工作流程:当一系列机制(main函数触发、checkpoint等)触发数据缓冲池中的脏页进行刷新到data file的时候,并不直接写磁盘,而是会通过memcpy函数将脏页先复制到内存中的double write buffer,之后通过double write buffer再分两次、每次1MB顺序写入共享表空间的物理磁盘上(ibdata)。然后马上调用fsync函数,同步脏页进磁盘上。由于在这个过程中,double write页的存储时连续的,因此写入磁盘为顺序写,性能很高;完成double write后,再将脏页写入实际的各个表空间文件,这时写入就是离散的了。如图所示:

     查看:show GLOBAL STATUS like '%innodb_dblwr%'

     Innodb_dblwr_pages_written :5166155016   #表示double write一共写了页的数量,可以用来数据的写入量

    Innodb_dblwr_writes :87244238  #实际写入的次数

    skip_innodb_doublewrite:用来禁用double write

    3.3 自适应哈希索引

    定义:Innodb存储引擎会监控对表上各个索引页的查询,如果觉得建立哈希索引能够带来性能的提升,则建立哈希索引,称为自适应哈希索引(AHI)。

    AHI是通过B+树构造而来,因此建立的速度很快,而且不需要对整个表构建哈希索引,Innodb会自动根据访问的频率和模式来自动地为某些热点建立哈希索引。

    AHI要求:对一个页的访问模式必须是一样的,访问模式一样,指的是查询条件一样。

    注意:AHI只用来搜索等值查询,例如where a=1

    查看:show engine innodb statusG

    INSERT BUFFER AND ADAPTIVE HASH INDEX
    -------------------------------------
    Ibuf: size 1, free list len 948959, seg size 948961, 870033008 merges
    merged operations:
     insert 4536471183, delete mark 24386361779, delete 4019249458
    discarded operations:
     insert 11214, delete mark 0, delete 0
    449.91 hash searches/s, 426.91 non-hash searches/s
    ---
    LOG

    3.4 异步IO

    异步IO(AIO):允许用户在发出一个IO请求后立即发送第二个IO请求,当全部的IO请求发送完毕后,等待所有的IO操作的完成。

    优势:可以合并多个IO,提高IPIOPS性能

    控制参数:innodb_use_native_aio

    3.5 刷新邻接页

    工作原理:当刷新一个脏页时,Innodb会检测到该页所在区的所有页,如果是脏页,则一起刷新

    四、Innodb启动与关闭

    4.1启动

    innobase_start_or_create_for_mysql函数用来控制innodb的启动,基本启动流程如下:

    1、初始化:

    同步控制系统,内存管理系统,日志恢复变量,后台线程同步控制系统

    根据innodb_buffer_pool_size和innodb_buffer_pool_instances初始化Buffer pool

    初始化日志系统,包括日志的写入,LSN的管理,检查点,日志刷盘以及日志恢复等

    IO异步线程,创建innodb_read_io_threads+innodb_write_io_threads个IO线程

    初始化日志恢复系统

    2、打开或者创建系统数据文件(ibdata)

    open_or_create_data_files函数控制,如果存在,则读取信息并打开,如果不存在,则创建,相当于初始化一个新的数据库实例。

    。。。。。。

    4.2关闭

    参考:《msyql技术内幕,Innodb存储引擎》

  • 相关阅读:
    图说jdk1.8新特性(3)--- 注解与类型推测优化
    图说jdk1.8新特性(2)--- Lambda
    图说jdk1.8新特性(1)--- 函数式接口
    Linux应用与端口
    Linu如何查看磁盘占用情况及处理办法
    ThreadLocal剧集(一)
    缓存穿透、缓存击穿、缓存雪崩区别和解决方案
    MySQL 新建用户,为用户授权,指定用户访问数据库
    2019面试总结
    Java的设计模式
  • 原文地址:https://www.cnblogs.com/zsql/p/11461342.html
Copyright © 2011-2022 走看看