• MySQL之6---存储引擎及事务


    MySQL之6---存储引擎及事务

    介绍

    存储引擎MySQL中的“文件系统”

    MySQL支持的存储引擎种类

    FEDERATED
    MEMORY
    InnoDB
    PERFORMANCE_SCHEMA
    MyISAM
    MRG_MYISAM
    BLACKHOLE
    CSV
    ARCHIVE
    

    彩蛋:请你列举MySQL中支持的存储引擎种类?
    InnoDB、MyISAM、CSV、MEMORY


    其它存储引擎

    分支产品的引擎种类介绍

    • PerconaDB:默认是XtraDB
    • MariaDB:默认是InnoDB
    • 其他引擎:TokuDB、MyRocks、Rocksdb
      • 特点:压缩比15倍以上,插入数据性能快3-5倍
      • 适应场景:Zabbix监控类的平台、归档库、历史数据存储业务

    img

    • Performance_Schema:Performance_Schema 数据库使用
    • Memory:将所有数据存储在RAM中,以便在需要快速查找参考和其他类似数据的环境中进行快速访问。适用存放临时数据。引擎以前被称为HEAP引擎
    • MRG_MyISAM:使MySQL DBA或开发人员能够对一系列相同的MyISAM表进行逻辑分组,并将它们作为一个对象引用。适用于VLDB(Very Large Data Base)环境,如数据仓库
    • Archive :为存储和检索大量很少参考的存档或安全审核信息,只支持SELECT和INSERT操作;支持行级锁和专用缓存区
    • Federated联合:用于访问其它远程MySQL服务器一个代理,它通过创建一个到远程MySQL服务器的客户端连接,并将查询传输到远程服务器执行,而后完成数据存取,提供链接单独MySQL服务器的能力,以便从多个物理服务器创建一个逻辑数据库。非常适合分布式或数据集市环境
    • BDB:可替代InnoDB的事务引擎,支持COMMIT、ROLLBACK和其他事务特性
    • Cluster/NDB:MySQL的簇式数据库引擎,尤其适合于具有高性能查找要求的应用程序,这类查找需求还要求具有最高的正常工作时间和可用性
    • CSV:CSV存储引擎使用逗号分隔值格式将数据存储在文本文件中。可以使用CSV引擎以CSV格式导入和导出其他软件和应用程序之间的数据交换
    • BLACKHOLE :黑洞存储引擎接受但不存储数据,检索总是返回一个空集。该功能可用于分布式数据库设计,数据自动复制,但不是本地存储
    • example:“stub”引擎,它什么都不做。可以使用此引擎创建表,但不能将数据存储在其中或从中检索。目的是作为例子来说明如何开始编写新的存储引擎

    InnoDB存储引擎特性

    img

    MVCC(Multi-Version Concurrency Control):多版本并发控制

    聚簇索引: 用来组织存储数据和优化查询,IOT。

    事务(Transaction): 数据安全保证

    行级锁(Row-level Lock): 控制并发

    外键

    多缓冲区支持

    AHI: 自适应Hash索引

    复制(Replication):Group Commit,GTID(Global Transaction ID),多线程(Multi-Threads-SQL)

    Hot Backup(热备份)

    CR Crash Recovery: 自动故障恢复

    DWB Double Write Buffer: 双写机制


    My1SAM 和InnoDB

    MyISAM InnoDB
    不支持事务 支持事务,适合处理大量短期事务
    表级锁,当表锁定时,其他人都无法使用,影响并发性范围大 行级锁
    读写相互阻塞,写入不能读,读时不能写 读写阻塞与事务隔离级别相关
    只缓存索引 可缓存数据和索引
    不支持外键约束 支持外键
    不支持聚簇索引 支持聚簇索引
    读取数据较快,占用资源较少 MySQL5.5后支持全文索引
    不支持MVCC(多版本并发控制机制)高并发 支持MVCC高并发
    崩溃恢复性差 崩溃恢复性好
    MySQL5.5.5前默认的数据库引擎 MySQL5.5.5后默认的数据库引擎
    适用只读(或者写较少)、表较小(可以接受长时间进行修复操作)的场景 系统表空间文件:ibddata1, ibddata2, ...
    tb_name.frm 表结构,tb_name.MYD 数据行,tb_name.MYI 索引 每表两个数据库文件:tb_name.frm 每表表结构,tb_name.ibd 数据行和索引

    彩蛋:InnoDB 核心特性有哪些? InnoDB和MyISAM区别有哪些?
    InnoDB支持事务、MVCC、聚簇索引、外键、缓冲区、AHI、CR、DW,MyISAM不支持。
    InnoDB支持行级锁,MyISAM支持表级锁。
    InnoDB支持热备(业务正常运行,影响低),MyISAM支持温备份(锁表备份)。
    InnoDB支持CR(自动故障恢复),宕机自动故障恢复,数据安全和一致性可以得到保证。MyISAM不支持,宕机可能丢失当前修改。


    案例1

    环境:

    zabbix 3.2 + centos 7.3 + mariaDB 5.5 InnoDB,zabbix监控了2000多个节点服务

    故障描述:

    每隔一段时间zabbix卡的要死,每隔3-4个月,都要重新搭建一遍zabbix,存储空间经常爆满。zabbix数据库500G,存在一个文件ibdata1里,手工删除1个月之前的数据,空间不释放。

    优化:

    • 数据库版本升级到percona 5.7+ 或者 mariadb 10.x+,zabbix升级更高版本
    • 存储引擎改为tokudb
    • 监控数据按月份进行切割(二次开发:zabbix 数据保留机制功能重写,数据库分表)
    • 关闭binlog和双1
    • 参数调整....

    为什么要这样优化?:
    (1)MariaDB 10.0.9原生态支持TokuDB,经过测试,5.7要比5.5 版本性能高2-3倍
    (2)TokuDB:insert数据比Innodb快的多,数据压缩比比Innodb高的多
    (3)监控数据按月份进行切割,为了能够truncate每个分区表,立即释放空间
    (4)关闭binlog:减少无关日志的记录.
    (5)参数调整:关闭安全性参数,提高性能.


    扩展部署:

    zabbix新版+ 新版本 tokudb VS zabbix + 低版本mariadb

    TokuDB独有的其他功能包括:

    • 高达25倍的数据压缩
    • 快速插入
    • 通过无读复制消除从机延迟
    • 热架构更改
    • 热索引创建 - TokuDB表支持插入、删除和查询,而索引添加到该表时没有停机时间
    • 热列添加、删除、扩展和重命名 — 当 alter table 添加、删除、扩展或重命名列时,TokuDB表支持不停机插入、删除和查询
    • 在线备份

    参考资料:
    Zabbix 3.0 for percona-server TokuDB:https://www.jianshu.com/p/898d2e4bd3a7
    MariaDB TokuDB:https://mariadb.com/kb/en/installing-tokudb/
    Percona TokuDB:https://www.percona.com/doc/percona-server/5.7/tokudb/tokudb_installation.html


    案例2

    环境:

    centos 5.8,MySQL 5.0 MyISAM,网站业务(LNMP),数据量50G左右

    故障描述:

    业务压力大的时候,非常卡;经历过宕机,会有部分数据丢失.

    问题分析:

    1. 监控锁的情况:有很多的表锁等待

    2. 存储引擎查看:所有表默认是MyISAM

    MyISAM存储引擎特性:

    1. 表级锁,在高并发时,会有很高锁等待

    2. 不支持事务,在断电时,会有可能丢失数据

    解决方案:

    1. 升级MySQL 5.6.1x版本
    2. 升级迁移所有表到新环境,调整存储引擎为InnoDB
    3. 开启双1安全参数
    4. 重构主从

    存储引擎基本操作

    查询支持的存储引擎

    SHOW ENGINES;
    

    查询默认存储引擎

    select @@default_storage_engine;
    

    设置默认存储引擎

    -- 会话级别(仅影响当前会话)
    set default_storage_engine=myisam;
    -- 全局级别(仅影响新会话)重启失效
    set global default_storage_engine=myisam;
    -- 写入配置文件,重启永久生效
    vim /etc/my.cnf
    [mysqld]
    default_storage_engine=InnoDB
    

    存储引擎是作用在表上的,也就意味着,不同的表可以有不同的存储引擎类型。

    查看库中所有表使用的存储引擎

    SHOW TABLE STATUS from db_name;
    

    查询指定表的存储引擎

    SHOW create table 表名;
    SHOW TABLE STATUS LIKE '%表名%';
    

    查询系统中所有业务表的存储引擎信息

    select table_schema, table_name, engine 
    from information_schema.tables  
    where table_schema not in ('sys','mysql','information_schema','performance_schema');
    

    创建表时设定存储引擎

    CREATE TABLE 表名 (id int) ENGINE=INNODB;
    

    修改已有表的存储引擎

    ALTER TABLE 库名.表名 ENGINE=MyISAM;
    

    案例3

    将所有的非InnoDB引擎的表查询出来,批量修改为InnoDB

    1. 查询:
    SELECT table_schema, table_name, ENGINE 
    FROM information_schema.tables  
    WHERE table_schema NOT IN ('sys','mysql','information_schema','performance_schema')
    AND ENGINE !='innodb';
    
    1. 开启导出文件功能
    vim /etc/my.cnf 
    [mysqld]
    secure-file-priv=/tmp
    
    1. 构建批量修改语句:
    SELECT CONCAT("alter table ",table_schema,".",table_name," engine=innodb;") 
    FROM information_schema.tables 
    WHERE table_schema NOT IN ('sys','mysql','information_schema','performance_schema') 
    AND ENGINE !='innodb' INTO OUTFILE '/tmp/a.sql';
    
    1. 执行批量修改语句:
    source /tmp/a.sql
    

    InnoDB磁盘结构(on-disk)

    磁盘文件(/data/mysql/data)

    ibdata1:系统数据字典信息(统计信息),UNDO表空间等数据

    ib_logfile0 ~ ib_logfile1: REDO日志文件,事务日志文件

    ibtmp1: 临时表空间磁盘位置,存储临时表

    frm:存储表的列信息

    ibd:表的数据行和索引

    myisam InnoDB 8.0之前
    .frm 数据字典 .frm 单表数据字典
    .myd 数据行 .ibd 数据行和索引
    .myi 索引 ibdata 共享表空间

    MySQL 8.0中删除.frm文件,其中数据存储在MySQL 8.0中引入的MySQL数据字典表中。

    MySQL数据字典的元数据实际上位于MySQL数据库目录中的InnoDB 每表文件表空间文件中。对于InnoDB数据字典,元数据实际上位于InnoDB 系统表空间中。

    从MySQL 8.0.3开始,InnoDB除临时表空间和撤消表空间文件外,所有表空间文件中都存在SDI 。表空间文件中SDI的存在提供了元数据冗余。例如,如果数据字典不可用,则可以使用ibd2sdi从表空间文件中提取字典对象元数据。


    表空间结构

    表空间的概念源于Oracle数据库。最初的目的是为了能够很好的做存储的扩容。

    到8.0.22版本为止,已经出现了很多表空间,具体介绍请查看官方文档:

    共享(系统)表空间

    存储方式 ibdata1~ibdataN, 5.5版本默认。

    共享表空间在各个版本存储内容的变化

    5.5版本:出现共享表空间
    系统相关:(全局)数据字典信息(表基本结构信息、状态、系统参数、属性..)、UNDO回滚日志(记录撤销操作)、Double Write Buffer信息、临时表信息、change buffer
    用户数据: 表数据行、表的索引数据

    5.6版本:共享表空间只存储于系统数据,把用户数据独立出去,由独立表空间管理。
    系统相关:(全局)数据字典信息、UNDO回滚信息、Double Write Buffer信息、临时表信息、change buffer

    InnoDB architecture diagram showing in-memory and on-disk structures.

    5.7版本:把临时表独立出去,UNDO回滚信息可以设定为独立出去
    系统相关:(全局)数据字典信息、UNDO回滚信息、Double Write Buffer信息、change buffer

    InnoDB architecture diagram showing in-memory and on-disk structures.

    8.0.11~8.0.19版本:UNDO回滚信息默认独立出去,数据字典信息不再集中存储在一个文件。
    系统相关:Double Write Buffer信息、change buffer

    8.0.20版本: 把Double Write Buffer信息独立出去
    系统相关:change buffer

    InnoDB architecture diagram showing in-memory and on-disk structures.


    共享表空间查看
    mysql> select @@innodb_data_file_path;
    +-------------------------+
    | @@innodb_data_file_path |
    +-------------------------+
    | ibdata1:12M:autoextend  |
    +-------------------------+
    1 row in set (0.00 sec)
    
    mysql> select @@innodb_autoextend_increment;
    +-------------------------------+
    | @@innodb_autoextend_increment |
    +-------------------------------+
    |                            64 |
    +-------------------------------+
    1 row in set (0.00 sec)
    

    含义:ibdata1文件,默认初始大小12M,不够用会自动扩展,默认每次扩展64M


    共享表空间扩容

    ① 初始化后设置共享表空间,重启生效。

    vim /etc/my.cnf
    [mysqld]
    innodb_data_file_path=ibdata1:12M;ibdata2:64M;ibdata3:64M:autoextend
    

    注意:ibdata1必须和当前文件时间大小一致

    错误处理:

    ibdata1设置值和当前文件实际大小不一致,重启数据库报错,查看日志文件

    tail -10 /data/3306/data/db01.err | grep ERROR
    ... ...
    [ERROR] InnoDB: The innodb_system data file './ibdata1' is of a different size 4864 pages (rounded down to MB) than the 768 pages specified in the .cnf file!
    ... ...
    

    实际大小:4864*16K/1024=76M

    my.cnf文件设置大小:768*16K/1024=12M

    查看ibdata1实际大小

    [root@db01 ~]# ls -lh /data/3306/data/ibdata1 
    -rw-r----- 1 mysql mysql 76M May  6 17:11 ibdata1
    

    ② 初始化前设置共享表空间(生产建议)

    5.7 中建议:设置共享表空间2-3个,大小建议1G或者4G,最后一个定制为自动扩展。

    8.0 中建议:设置1-2个就ok,大小建议1G或者4G,最后一个定制为自动扩展。


    独立(每表文件)表空间

    ​ 从5.6开始,出现了独立表空间,包含单个InnoDB表的数据和索引 ,并存储在文件系统中自己的数据文件中,一个表一个ibd文件单独管理,8.0版本删除了frm文件。


    独立表空间控制
    -- 查看控制参数
    select @@innodb_file_per_table;
    -- 独立表空间存储用户数据,创建表生成ibd文件
    set global innodb_file_per_table=1;
    -- 共享表空间存储用户数据,创建表不生成ibd文件
    set global innodb_file_per_table=0;
    

    独立表空间同版本快速迁移数据

    源端:/data/3306/data/t100w -----> 目标端:/data/3307/data/t100w

    1. 源端锁定t100w表

      lock

      flush

    -- 获取写锁
    mysql> lock tables test.t100w write;
    -- 或者刷新并获取表的读锁
    mysql> flush tables test.t100w with read lock;
    
    1. 源端查看t100w表创表语句
    mysql> show CREATE TABLE test.t100w;
    CREATE TABLE `t100w` (
      `id` int(11) DEFAULT NULL,
      `num` int(11) DEFAULT NULL,
      `k1` char(2) DEFAULT NULL,
      `k2` char(4) DEFAULT NULL,
      `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
    
    1. 目标端创建test库和t100w空表
    mysql> CREATE database test charset=utf8mb4;
    mysql> CREATE TABLE `t100w` (
      `id` int(11) DEFAULT NULL,
      `num` int(11) DEFAULT NULL,
      `k1` char(2) DEFAULT NULL,
      `k2` char(4) DEFAULT NULL,
      `dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
    
    1. 目标端删除空的表空间文件
    mysql> alter table test.t100w discard tablespace;
    
    1. 拷贝源端ibd文件到目标端目录,并设置权限
    [root@db01 ~]# cp /data/3306/data/test/t100w.ibd /data/3307/data/test/
    [root@db01 ~]# chown -R mysql.mysql /data/*
    
    1. 目标端导入表空间
    mysql> alter table test.t100w import tablespace;
    
    1. 目标端验证结果
    mysql> select count(*) from test.t100w;
    +----------+
    | count(*) |
    +----------+
    |  1000000 |
    +----------+
    
    1. 源端解锁数据表
    mysql> unlock tables;
    

    案例4

    环境:

    联想服务器(IBM),磁盘500G,没有raid,centos 6.8,mysql 5.6.33 innodb,没有备份,没开二进制日志,没有主从,LNMT架构。开发用户专用库:jira(bug追踪) 、 confluence(内部知识库)

    故障描述:

    突然断电,启动后发现文件系统/只读,fsck 修复文件系统并重启,系统成功启动,mysql启动不了。结果:confulence库在,jira库不见了

    需求:

    暂时把confulence库先打开用着,直接访问时访问不了的,需要将生产库confulence,拷贝到1:1虚拟机上的/var/lib/mysql

    解决方案:独立表空间迁移

    create table confulence.t1;
    alter table confulence.t1 discard tablespace;
    alter table confulence.t1 import tablespace;
    

    处理流程:

    /*1、confulence库中一共有107张表,创建107张和原来一模一样的空表。
    他有2016年的历史库,我让他去他同时电脑上mysqldump备份confulence库*/
    # mysqldump -uroot -ppassword -B confulence --no-data >test.sql
    -- 拿到你的测试库,进行恢复,到这步为止,表结构有了。
    -- 2、删除表空间。
    select concat('alter table ',table_schema,'.'table_name,' discard tablespace;') from information_schema.tables where table_schema='confluence' into outfile '/tmp/discad.sql';
    source /tmp/discard.sql
    -- 3、拷贝生产中confulence库下的所有表的ibd文件到准备好的环境中,导入表空间
    select concat('alter table ',table_schema,'.'table_name,' import tablespace;') from information_schema.tables where table_schema='confluence' into outfile '/tmp/discad.sql';
    source /tmp/discad.sql
    -- 4、验证数据
    -- 表都可以访问了,数据挽回到了出现问题时刻的状态
    

    获取表结构

    8.0之前

    可以使用MySQL Utilities提供的mysqlfrm用来读取.frm文件,并从该文件中找到表定义数据,生成CREATE语句。

    cd /opt
    wget https://downloads.mysql.com/archives/get/p/30/file/mysql-utilities-1.6.5.tar.gz
    tar -xvzf mysql-utilities-1.6.5.tar.gz
    python /opt/mysql-utilities-1.6.5/setup.py build
    python /opt/mysql-utilities-1.6.5/setup.py install
    # 获取独立表空间的表结构
    mysqlfrm --diagnostic 表名.frm | grep -v "^#" > /tmp/db_table.sql
    

    注意:.frm文件中没有外键约束和自增长序列的信息

    删除表空间前可以设置跳过外键检查来规避问题

    set foreign_key_checks=0
    

    8.0之后

    可以使用ibd2sdi离线的将ibd文件中的冗余存储的SDI信息提取出来,并以json的格式输出到终端。

    参考文章:英文原文 中文翻译

    1. 把 表名.ibd 中的表结构以json的格式输出到 dbsdi.json文件
    ibd2sdi --dump-file=dbsdi.json  表名.ibd
    

    注意:当存在中文注释时,解析出来的注释可能是乱码的,而且大概率会触发ibd2sdi的bug(中文乱码导致json格式错误,比如缺少引号)。

    此时可以使用vscode打开dbsdi.json,vscode会高亮json文件格式正确的部分,手动修复不正确的格式,保存。

    1. 使用jq提取json里的数据

      • CentOS 使用yum安装

        通用命令

        ibd2sdi 表名.ibd |jq  '.[]?|.[]?|.dd_object?|({table:.name?},(.columns?|.[]?|{name:.name,type:.column_type_utf8}))' > dbsdi-jq.json
        
      • Windows 下载可执行文件安装

        Powershell调用jq解析json文件

        Get-Content -Path dbsdi.json |jq  '.[]?|.[]?|.dd_object?|({table:.name?},(.columns?|.[]?|{name:.name,type:.column_type_utf8}))' > dbsdi-jq.json
        

    撤销表空间

    存储撤消日志,用来回滚事务。

    撤销表空间查看配置参数
    -- 打开独立undo模式,并设置undo的个数,建议3-5个,8.0弃用
    SELECT @@innodb_undo_tablespaces;
    -- undo日志的大小,默认1G
    SELECT @@innodb_max_undo_log_size;
    -- 开启undo自动回收的机制(undo_purge)
    SELECT @@innodb_undo_log_truncate;
    -- 触发自动回收的条件,单位是检测次数
    SELECT @@innodb_purge_rseg_truncate_frequency;
    -- undo文件存储路径
    SELECT @@innodb_undo_directory;
    
    撤销表空间配置

    5.7版本

    默认存储在共享表空间中(ibdataN),生产中必须手工独立出来,否则影响高并发效率。

    只能在初始化时配置undo个数,并且是固定的。

    # 1.创建目录
    [root@db01 ~]# mkdir /data/3357/{data,etc,socket,log,pid,undologs} -pv
    [root@db01 ~]# chown -R mysql. /data/*
    
    # 2.添加参数
    [root@db01 ~]# vim /data/3357/my.cnf
    [mysqld]
    innodb_undo_tablespaces=3           
    innodb_max_undo_log_size=128M
    innodb_undo_log_truncate=ON
    innodb_purge_rseg_truncate_frequency=32
    innodb_undo_directory=/data/3357/undologs
    
    # 3.初始化数据库
    [root@db01 ~]# /usr/local/mysql57/bin/mysqld --defaults-file=/data/3357/my.cnf  --initialize-insecure --user=mysql --basedir=/usr/local/mysql57 --datadir=/data/3357/data
    
    # 4.启动数据库
    [root@db01 ~]# /etc/init.d/mysqld start
    
    # 5.查看结果
    [root@db01 ~]# ll /data/3357/undologs/
    -rw-r----- 1 mysql mysql 10485760 May  11 15:39 /data/3357/undologs/undo001
    -rw-r----- 1 mysql mysql 10485760 May  11 15:39 /data/3357/undologs/undo002
    -rw-r----- 1 mysql mysql 10485760 May  11 15:39 /data/3357/undologs/undo003
    

    8.0版本

    默认就是独立的(undo_001-undo_002),可以随时配置,innodb_undo_tablespaces选项已过时。

    -- 查询所以表空间文件
    SELECT TABLESPACE_NAME, FILE_NAME FROM INFORMATION_SCHEMA.FILES;
    
    -- 查询undo表空间
    SELECT TABLESPACE_NAME, FILE_NAME FROM INFORMATION_SCHEMA.FILES WHERE FILE_TYPE LIKE 'UNDO LOG';
    
    -- 添加undo表空间
    CREATE UNDO TABLESPACE tablespace_name ADD DATAFILE 'file_name.ibu';
    
    -- 删除undo表空间
    -- 必须为空,先标记为非活动状态,再删除
    ALTER UNDO TABLESPACE tablespace_name SET INACTIVE;
    DROP UNDO TABLESPACE tablespace_name;
    
    -- 监视undo表空间的状态
    SELECT NAME, STATE FROM INFORMATION_SCHEMA.INNODB_TABLESPACES WHERE NAME LIKE 'tablespace_name';
    

    临时表空间

    5.7版本

    临时表空间(ibtmp1)用于存储临时表。建议数据初始化之前设定好,一般2-3个,大小512M-1G。

    临时表空间查看配置参数

    mysql> select @@innodb_temp_data_file_path;
    +------------------------------+
    | @@innodb_temp_data_file_path |
    +------------------------------+
    | ibtmp1:12M:autoextend        |
    +------------------------------+
    

    配置文件设置,重启生效

    [root@db01 ~]# vim /etc/my.cnf
    [mysqld]
    innodb_temp_data_file_path=ibtmp1:12M;ibtmp2:128M:autoextend:max:500M
    

    8.0版本

    分为会话临时表空间和全局临时表空间

    • 会话临时表空间(temp_N.ibt)用于存储临时表。

      位置参数

      mysql> select @@innodb_temp_tablespaces_dir;
      +-------------------------------+
      | @@innodb_temp_tablespaces_dir |
      +-------------------------------+
      | ./#innodb_temp/               |
      +-------------------------------+
      
    • 全局临时表空间(ibtmp1)用于存储对用户创建的临时表进行更改的回滚段。

      配置同5.7版本的临时表空间


    重做日志(Redo Log)

    Redo Log 记录内存数据页的变化(数据页的变化信息+数据页当时的LSN号)。实现“前滚”的功能。

    存储在数据路径下(ib_logfile0,ib_logfile1,...),轮序覆盖记录日志。

    刷新策略:commit提交后,刷新当前事务的 redo buffer 到磁盘,还会顺便将一部分 redo buffer 中没有提交的事务日志也刷新到磁盘。

    WAL(write ahead log):保证 Redo Log 优先于数据写入磁盘。


    查询配置参数

    mysql> show variables like '%innodb_log_file%';
    +---------------------------+----------+
    | Variable_name             | Value    |
    +---------------------------+----------+
    | innodb_log_file_size      | 50331648 |
    | innodb_log_files_in_group | 2        |
    +---------------------------+----------+
    

    设置

    生产建议: 设置3-5组,512M-4G

    配置文件添加参数,重启生效

    [root@db01 ~]# vim /etc/my.cnf 
    [mysqld]
    innodb_log_file_size=100M
    innodb_log_files_in_group=3
    

    回滚日志(undo log)

    Undo Log 是撤消日志的集合,提供快照技术,保存事务修改之前的数据状态,保证了MVCC,隔离性,mysqldump的热备。

    • 在rolback时,将数据恢复到修改之前的状态。
    • 在实现CSR时,回滚到redo当中记录的未提交的时候。

    5.7版本,存储在共享表空间中 (ibdata1~ibdataN

    8.0版本

    对常规表执行操作的事务的撤消日志存储在撤消表空间中(undo_001-undo_002)。
    对临时表执行操作的事务的撤消日志存储在全局临时表空间中(ibtmp1)。

    每个撤消表空间和全局临时表空间分别支持最多128个回滚段。

    配置回滚段的数量

    select @@innodb_rollback_segments;
    

    双写缓冲区 Double Write Buffer(DWB)

    双写缓冲区是一个存储区域,InnoDB先将从页面缓冲池中刷新的页面写入双写缓冲区,然后再将页面写入InnoDB数据文件中。

    如果在页面写入过程中,发生操作系统,存储子系统或mysqld进程的意外退出,则InnoDB可以在崩溃恢复期间从doublewrite缓冲区中找到页面的良好副本。

    8.0.19前默认位于ibdataN中,8.0.20后就独立出来位于#*.dblwr


    预热文件(ib_buffer_pool)

    用来缓冲和缓存“热”(经常查询或修改)数据页,减少物理IO。MySQL 5.7默认启用。

    当关闭数据库的时候,缓冲和缓存会失效。5.7版本后,MySQL正常关闭时,会将内存的热数据存放(流方式)至ib_buffer_pool。下次重启直接读取ib_buffer_pool加载到内存中。

    查询配置参数

    指定在关闭MySQL服务器时是否记录InnoDB 缓冲池中缓存的页面 ,以缩短下次重启时的预热过程。innodb_buffer_pool_dump_pct 选项定义要转储的最近使用的缓冲池页面的百分比。

    select @@innodb_buffer_pool_dump_at_shutdown;
    select @@innodb_buffer_pool_load_at_startup;
    

    InnoDB内存结构

    缓冲池 InnoDB BUFFER POOL(IBP)

    缓冲池主要用来缓冲、缓存MySQL的数据页和索引页,还有AHI、Change buffer。MySQL中最大的、最重要的内存区域。

    配置InnoDB缓冲池大小

    -- 查看缓存池大小,默认128M
    mysql> select @@innodb_buffer_pool_size;
    +---------------------------+
    | @@innodb_buffer_pool_size |
    +---------------------------+
    |                 134217728 |
    +---------------------------+
    

    生产建议:物理内存的:50-80%

    全局设置: 重新连接mysql生效。

    set global innodb_buffer_pool_size=268435456;
    

    永久设置:配置文件添加参数,重启mysql生效

    vim /etc/my.cnf 
    [mysqld]
    innodb_buffer_pool_size=256M
    

    配置多个缓冲池实例

    -- 查询缓冲池实例数量,默认1,最大为64
    mysql> select @@innodb_buffer_pool_instances;
    +--------------------------------+
    | @@innodb_buffer_pool_instances |
    +--------------------------------+
    |                              1 |
    +--------------------------------+
    

    注意:仅当您将innodb_buffer_pool_size大小设置为1GB或更大时,此选项才生效,是所有缓冲池实例大小之和。

    为了获得最佳效率,请组合 innodb_buffer_pool_instancesinnodb_buffer_pool_size使得每个缓冲池实例是至少为1GB。


    日志缓冲区 InnoDB LOG BUFFER (ILB)

    用于保存要写入磁盘上的日志文件(Redo Log)的数据。

    查询配置参数

    select @@innodb_log_buffer_size;
    

    ​ 默认大小:16M
    ​ 生产建议:innodb_log_file_size的1-N倍
    永久设置:配置文件添加参数,重启mysql生效

    vim /etc/my.cnf
    [mysqld]
    innodb_log_buffer_size=33554432
    

    事务(Transactions)

    事务:一组原子性的SQL语句,或一个独立工作单元,由一系列动作组合起来的一个完整的整体。

    事务日志:记录事务信息,实现undo,redo等故障恢复功能

    redo:当我们正在想数据库中存放数据,或写入数据时,此时出现断电情况,数据只执行了一半,电源连上之后发现此时的两个数据都只做了一半,就会执行redo功能,重新执行。

    undo:当我们正在想数据库中存放数据,或写入数据时,此时出现断电情况,前两个数据已经执行完毕,但是第三个数据未传完,电源连上之后发现此时还有后续的完整数据未执行,就会执行undo功能,就会取消执行。


    事务的ACID特性

    • Atomic(原子性)

    整个事务中的所有操作要么全部成功执行,要么全部失败后回滚。不能出现中间状态。

    • Consistent(一致性)

    如果数据库在事务开始时处于一致状态,则在执行该事务期间将保留一致状态,总是从一个一致性状态转换为另一个一致性状态。

    • Isolated(隔离性)

    事务之间不相互影响。

    • Durable(持久性)

    事务成功完成后,所做的所有更改都会永久地记录在数据库中。

    事务的生命周期(控制语句)

    标准事务控制语句

    启动事务

    BEGIN;
    START TRANSACTION;
    

    结束事务

    -- 提交事务
    COMMIT;
    -- 回滚事务
    ROLLBACK;
    

    注意:事务生命周期中,只能使用DML语句(select、update、delete、insert)


    自动提交(autocommit)

    -- 查询自动提交设置状态,默认开启值为1
    select @@autocommit;
    -- 临时会话设置
    set autocommit=0;
    -- 临时全局设置
    set global autocommit=0;
    -- 永久设置
    vim /etc/my.cnf
    autocommit=0
    

    如果开启自动提交,则对表的所有更改将立即生效。每个SQL语句形成一个事务,如果该SQL语句未返回错误,则MySQL在每个SQL语句之后进行提交。如果一条语句返回错误,则提交或回滚行为取决于该错误

    要使用多语句事务,请关闭自动提交功能。要保留自动提交功能,请显式使用事务控制语句。

    导致隐式提交的非事务语句

    • DDL语句: ALTER、CREATE 和 DROP、TRUNCATE TABLE、...
    • DCL语句: GRANT、REVOKE 和 SET PASSWORD、...
    • 事务控制和锁定语句:BEGIN、LOCK TABLES 和 UNLOCK TABLES、...
    • 数据加载语句:LOAD DATA、...
    • 行政声明:FLUSH、LOAD INDEX INTO CACHE 和 OPTIMIZE TABLE、...
    • 复制控制语句:START REPLICA | SLAVE, STOP REPLICA | SLAVE, RESET REPLICA | SLAVE, CHANGE MASTER TO.

    导致隐式回滚的执行错误

    • 磁盘空间不足,回滚失败的语句
    • 重复键错误,回滚失败的语句
    • row too long error,回滚失败的语句
    • 出现事务冲突(死锁),回滚整个事务
    • 会话窗口被关闭
    • 数据库关闭

    建议:生产中显式请求和提交事务,不要使用“自动提交”功能,可以很大程度上提高数据库性能


    事务使用流程

    -- 检查autocommit是否为关闭状态
    select @@autocommit;
    -- 开始事务
    begin;
    -- DML语句
    delete from student where name='alexsb';
    -- 建立保存点sp1
    savepoint sp1
    -- DML语句
    update student set name='alexsb' where name='alex';
    -- 回滚到保存点sp1
    ROLLBACK To sp1
    -- 回滚结束
    ROLLBACK;
    -- 或者
    -- 提交结束
    commit;
    

    事务的隔离级别

    事务隔离实现事务工作期间的“读”的隔离,处理MVCC,读一致性问题

    隔离级别类型

    1. RU:READ-UNCOMMITTED 读未提交,可以读取到事务未提交的数据。
      • 优点:事务的并发度最高
      • 缺点:隔离性差,会出现脏读(当前内存读),不可重复读,幻读问题
    2. RC:READ-COMMITTED 读已提交(常用),可以读取到事务已提交的数据。
      • 优点:事务的并发度较好,防止脏读
      • 缺点:隔离性一般,会出现不可重复读,幻读问题
    3. RR:REPEATABLE-READ 可重复读(默认)
      • 优点:事务的并发度一般,防止脏读,防止不可重复读
      • 缺点:隔离性较好,会出现幻读问题
    4. SR:SERIALIZABLE 可串行化
      • 优点:隔离性最好,可以防止死锁,主要用于InnoDB存储引擎的分布式事务。
      • 缺点:事务没有并发
    RC  可以减轻GAP+NextLock锁的问题,一般在为了读一致性会在正常select后添加for update语句,记住执行完一定要commit,否则容易出现严重锁等待。
    RR  利用的是undo的快照技术+GAP(间隙锁)+NextLock(下键锁)
    

    实现隔离机制的方法主要有两种:

    1. 加读写锁
    2. 一致性快照读,即 MVCC

    本质上,隔离级别是一种在并发性能和并发产生的副作用间的妥协,通常数据库均倾向于采用 Weak Isolation


    隔离级别参数

    select @@transaction_isolation;
    set global transaction_isolation='READ-UNCOMMITTED';
    set global transaction_isolation='READ-COMMITTED';
    set global transaction_isolation='REPEATABLE-READ';
    set global transaction_isolation='SERIALIZABLE';
    
    vim /etc/my.cnf
    [mysqld]
    transaction_isolation='READ-COMMITTED';
    

    问题现象演示

    -- 创建测试库
    create database test;
    -- 创建测试表
    create table test.t1 (
    id int not null primary key auto_increment ,
    a  int not null ,
    b  varchar(20) not null, 
    c  varchar(20) not null 
    )charset=utf8mb4 engine=innodb;
    
    begin;
    insert into test.t1(a,b,c) 
    values
    (1,'a','aa'),
    (2,'c','ab'),
    (3,'d','ae'),
    (4,'e','ag'),
    (5,'f','at');
    commit;
    -- 关闭自动提交
    set global autocommit=0;
    -- 打开两个会话窗口:
    -- sessionA: 
    -- sessionB: 
    

    脏读

    脏读又称无效数据的读出,当前内存读,可以读取到别人未提交的数据。

    例如:事务T1修改某一值,未提交,但是事务T2却能读取该值,此后T1因为某种原因撤销对该值的修改,这就导致了T2所读取到的数据是无效的。注意,脏读一般是针对于update操作。

    -- RU级别下不可重读现象演示:
    -- 第一步:设置隔离级别,重新连接数据库
    mysql> set global transaction_isolation='READ-UNCOMMITTED';
    mysql> exit
    -- 第二步:检查隔离级别
    -- sessionA: 
    mysql> select @@transaction_isolation;
    +-------------------------+
    | @@transaction_isolation |
    +-------------------------+
    | READ-UNCOMMITTED        |
    +-------------------------+
    -- sessionB: 
    mysql> select @@transaction_isolation;
    +-------------------------+
    | @@transaction_isolation |
    +-------------------------+
    | READ-UNCOMMITTED        |
    +-------------------------+
    -- 第三步:开启事务
    -- sessionA: 
    mysql> begin;
    -- sessionB: 
    mysql> begin;
    -- 第四步:查看当前表数据
    -- sessionA: 
    mysql> select * from test.t1 where id=2;
    +----+---+---+----+
    | id | a | b | c  |
    +----+---+---+----+
    |  2 | 2 | c | ab |
    +----+---+---+----+
    -- sessionB: 
    mysql> select * from test.t1 where id=2;
    +----+---+---+----+
    | id | a | b | c  |
    +----+---+---+----+
    |  2 | 2 | c | ab |
    +----+---+---+----+
    -- 第五步:
    -- sessionA: 执行DML语句
    mysql> update test.t1 set a=8 where id=2;
    -- 第六步:
    -- sessionB:查看当前表数据发现数据变化,脏读
    mysql> select * from test.t1 where id=2;
    +----+---+---+----+
    | id | a | b | c  |
    +----+---+---+----+
    |  2 | 8 | c | ab |
    +----+---+---+----+
    -- 第七步:
    -- sessionA: 回滚
    mysql> rollback;
    -- 第八步:
    -- sessionB:查看当前表数据发现数据变化,不可重复读
    mysql> select * from test.t1 where id=2;
    +----+---+---+----+
    | id | a | b | c  |
    +----+---+---+----+
    |  2 | 2 | c | ab |
    +----+---+---+----+
    

    不可重复读

    不可重复读,指一个事务范围内两个相同的查询却返回了不同数据。

    这是由于查询时系统中其他事务修改的提交而引起的。比如事务T1读取某一数据,事务T2读取并修改了该数据,T1为了对读取值进行检验而再次读取该数据,便得到了不同的结果。

    -- RC级别下不可重读现象演示:
    -- 第一步:设置隔离级别,重新连接数据库
    mysql> set global transaction_isolation='READ-COMMITTED';
    mysql> exit
    -- 第二步:检查隔离级别
    -- sessionA: 
    mysql> select @@transaction_isolation;
    +-------------------------+
    | @@transaction_isolation |
    +-------------------------+
    | READ-COMMITTED          |
    +-------------------------+
    -- sessionB: 
    mysql> select @@transaction_isolation;
    +-------------------------+
    | @@transaction_isolation |
    +-------------------------+
    | READ-COMMITTED          |
    +-------------------------+
    -- 第三步:开启事务
    -- sessionA: 
    mysql> begin;
    -- sessionB: 
    mysql> begin;
    -- 第四步:查看当前表数据
    -- sessionA: 
    mysql> select * from test.t1 where id=1;
    +----+---+---+----+
    | id | a | b | c  |
    +----+---+---+----+
    |  1 | 1 | a | aa |
    +----+---+---+----+
    -- sessionB: 
    mysql> select * from test.t1 where id=1;
    +----+---+---+----+
    | id | a | b | c  |
    +----+---+---+----+
    |  1 | 1 | a | aa |
    +----+---+---+----+
    -- 第五步:
    -- sessionA: 执行DML语句并提交事务
    mysql> update test.t1 set a=6 where id=1;
    mysql> commit;
    -- 第六步:
    -- sessionB:查看当前表数据发现数据变化
    mysql> select * from test.t1 where id=1;
    +----+---+---+----+
    | id | a | b | c  |
    +----+---+---+----+
    |  1 | 6 | a | aa |
    +----+---+---+----+
    

    幻读

    幻读 ,指同一查询在不同时间产生不同的行集。

    例如:第一个事务对一个表中的数据进行了修改,比如这种修改涉及到表中的“全部数据行”。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入“一行新数据”。那么,就会发生操作第一个事务的用户发现表中还存在没有修改的数据行,就好象发生了幻觉一样。

    一般解决幻读的方法是增加范围锁RangeS,锁定检索范围为只读,这样就避免了幻读。

    -- RC级别下幻读现象演示:
    -- 第一步:设置隔离级别,重新连接数据库
    mysql> set global transaction_isolation='READ-COMMITTED';
    mysql> exit
    -- 第二步:检查隔离级别
    -- sessionA: 
    mysql> select @@transaction_isolation;
    +-------------------------+
    | @@transaction_isolation |
    +-------------------------+
    | READ-COMMITTED          |
    +-------------------------+
    -- sessionB: 
    mysql> select @@transaction_isolation;
    +-------------------------+
    | @@transaction_isolation |
    +-------------------------+
    | READ-COMMITTED          |
    +-------------------------+
    -- 第三步:开启事务
    -- sessionA: 
    mysql> begin;
    -- sessionB: 
    mysql> begin;
    -- 第四步:查看当前表数据
    -- sessionA: 
    mysql> select * from test.t1;
    +----+---+---+----+
    | id | a | b | c  |
    +----+---+---+----+
    |  1 | 6 | a | aa |
    |  2 | 2 | c | ab |
    |  3 | 3 | d | ae |
    |  4 | 4 | e | ag |
    |  5 | 5 | f | at |
    +----+---+---+----+
    -- sessionB: 
    mysql> select * from test.t1;
    +----+---+---+----+
    | id | a | b | c  |
    +----+---+---+----+
    |  1 | 6 | a | aa |
    |  2 | 2 | c | ab |
    |  3 | 3 | d | ae |
    |  4 | 4 | e | ag |
    |  5 | 5 | f | at |
    +----+---+---+----+
    -- 第五步:
    -- sessionA:执行DML语句,全部数据行修改
    mysql> update test.t1 set a=10 where a<10;
    -- 第六步:
    -- sessionB:执行DML语句,插入一行数据,提交事务
    mysql> insert into test.t1(a,b,c) values (1,'z','az');
    mysql> commit;
    -- 第七步: 
    -- sessionA:提交事务
    mysql> commit;
    -- 第八步:
    -- sessionA:查看当前表数据,好像发生了幻觉
    mysql> select * from test.t1;
    +----+----+---+----+
    | id | a  | b | c  |
    +----+----+---+----+
    |  1 | 10 | a | aa |
    |  2 | 10 | c | ab |
    |  3 | 10 | d | ae |
    |  4 | 10 | e | ag |
    |  5 | 10 | f | at |
    |  6 |  1 | z | az |
    +----+----+---+----+
    

  • 相关阅读:
    再理解HDFS的存储机制
    C实现头插法和尾插法来构建单链表(不带头结点)
    linux系统编程:线程同步-相互排斥量(mutex)
    基于github for windows&amp;github的团队协作基本操作
    分治法求众数问题 (配图)
    hdu1576 mod 运算的逆元
    Android5.0(lollipop)新特性介绍(一)
    jenkins详解(一)
    手机APP测试点总结
    App测试方法总结
  • 原文地址:https://www.cnblogs.com/backups/p/mysql_6.html
Copyright © 2020-2023  润新知