事务

确保事务内的SQL都可以同步执行 要么一起成功 要么一起失败。事务有四个特性原子性 一致性,隔离性,持久性

实现方式

开始事务的时候回家记录记录一个LSN日志序列 当事务执行的时候 会首先在Innodb_log_buffer 日志缓冲区插入事务日志 redo log;当事务提交的时候 再根据不同的策略将缓冲日志刷新到日志文件和磁盘中。
  • 将数据写入InnoDB buffer pool 并加上独占锁
  • 将UNDO信息写入undo表的回滚段 以备回滚数据的时候使用
  • 更改缓存页中的数据 并将更新记录写入redo buffer
  • 提交时 根据innodb_flush_log_at_trx_commit的设置 用不同的方式将redo buffer中的数据刷新到 redo log file中 然后释放独占锁
  • 最后 后台IO线程根据将缓存中的数据刷新到磁盘

innodb_flush_log_at_trx_commit

默认设置为1  既在每次事务提交的时候 都会将缓冲池中的数据写入到日志文件中 并立即调用系统fsync 刷新日志文件到磁盘
设置为0 每秒钟执行数据写入日志并调用系统fsync
设置为2 只在事务提交的时候 属性buffer中的redo 数据写入日志文件中 但是将日志文件写入到磁盘 则由系统配置确认

innodb_log_buffer_size

决定了重做日志缓存的大小 如果是无语中有大量的插入或者更新数据 则需要调整默认配置 以提高性能

Redo log 重做日志缓冲

主要是解决 提交的事务没有执行完成但是数据库奔溃了,当数据库恢复之后,可以完整的恢复数据。
InnoDB存储引擎会首先将重做日志信息放到这个缓冲区 redo log buffer,然后按照不同的策略和频率将buffer中的数据刷新到重做日志中。 redo log 在磁盘中保存的名称为 ib_logfile0 and ib_logfile1

Checkpoing技术

当事务提交时 需要先写重做日志 然后再修改页。如果数据库由于未知原因崩溃 而导致数据丢失的时候 需要通过重做日志来完成

当数据库发生宕机的时候 数据的恢复 不需要重做所有的日志 这个时候就用到checkpoint。数据恢复的时候 只要恢复Checkpoint这个点,之后的数据即可。

对于InnoDB存储引擎来来说 有一个LSN的数字 来标记版本的

数据库并发

相对于串行处理 支持并发可以提高数据库的利用率 但是数据库并发可能会带来下面的问题
  • 更新丢失 例如同一条数据 被不同事物更新 导致 有一个事务更新错误
  • 脏读 事务更新的数据 还未提交 就被另外一个事务读取 读取的数据 就有可能是脏数据
  • 不可重复读 前后两次读取数据的过程中 数据被另外的事务修改 导致数据不一致
  • 幻读 解决了不可重复读 但是如果另外一个事务同时提交了新的数据

innodb隔离级别

MariaDB [(none)]> show variables like "%tx_isolation%";
+---------------+-----------------+
| Variable_name | Value |
+---------------+-----------------+
| tx_isolation | REPEATABLE-READ |
+---------------+-----------------+
在同一个事务内的查询 与事务开始时刻的数据 保持一致 ,但是存在幻读

幻读演示

session1 开启事务

mysql> begin;
Query OK, 0 rows affected mysql> select * from phone; ##事务开始的时候 电话是123456的数据只有一条数据
+----+----------+
| id | phone |
+----+----------+
| 3 | 123456 |
+----+----------+

session2 如果是事务添加的话 也是一样的结果

## 这里插入一条新的数据 电话也是 123456
INSERT INTO `tmp`.`phone` (`id`, `phone`) VALUES ('2', '123456');

session1

mysql> select *  from phone;    ##这里再次读取数据 和事务开始的时候 数据相同 电话是123456的数据只有一条数据
+----+----------+
| id | phone |
+----+----------+
| 3 | 123456 |
+----+----------+ ## 这里更新数据的时候 显示有2行数据被更新了
mysql> update phone set phone=111111 where phone=123456;
Query OK, 2 rows affected
Rows matched: 2 Changed: 2 ## 这个时候 数据库有两条数据 被更新成111111了 出现了幻读
mysql> select * from phone;
+----+----------+
| id | phone |
+----+----------+
| 2 | 111111 |
| 3 | 111111 |

解决方式 加锁

Session1 开启一个事务 并通过for update的方式加锁

mysql> begin;

mysql> select *  from phone for update;

Session2 开启一个事务

mysql> INSERT INTO `tmp`.`phone` (`id`, `phone`) VALUES ('2', '123456');

1205 - Lock wait timeout exceeded; try restarting transaction

## 锁等待超时 退出事务

MVCC

多版本并发控制协议 优点是 读不加锁 读写不冲突。InnoDB通过Undo Log实现了数据的多版本。而并发控制则是通过锁来实现。读操作可以分为两种 一种是快照读另外一种是当前读

快照读:读取的是记录的可见版本 也有可能是历史版本 不用加锁。
当前读:读取的是记录的最新版本 InnoDB实现的MVVC没有解决幻读 可以通过加锁的方式解决这个问题

innodb buffer pool

参考博客 :https://michael.bouvy.net/blog/en/2015/01/18/understanding-mysql-innodb-buffer-pool-size/

查看包含的数据类型及大小

SELECT
page_type AS Page_Type,
sum(data_size) / 1024 / 1024 AS Size_in_MB
FROM
information_schema.innodb_buffer_page
GROUP BY
page_type
ORDER BY
Size_in_MB DESC;

结果

+-------------------+------------+
| Page_Type | Size_in_MB |
+-------------------+------------+
| INDEX | 0.13445091 |
| UNKNOWN | 0.00000000 |
| INODE | 0.00000000 |
| IBUF_INDEX | 0.00000000 |
| TRX_SYSTEM | 0.00000000 |
| SYSTEM | 0.00000000 |
| UNDO_LOG | 0.00000000 |
| FILE_SPACE_HEADER | 0.00000000 |
| IBUF_BITMAP | 0.00000000 |
+-------------------+------------+

一些重要的且常用的数据类型解释

  • INDEX:B-Tree 索引 这里应该也包含行数据页的数据 因为聚簇索引中 主键索引和数据是放在一起的
  • IBUF_INDEX: 插入缓存索引(下文 innodb_change_buffering)
  • UNKNOWN:未被使用或者不知道状态的
  • TRX_SYSTEM:系统数据(有可能是指事务 这里不确定)
  • UNDO_LOG:Undo日志(下文 Undo log)

innodb_change_buffering

https://dev.mysql.com/doc/refman/5.5/en/innodb-performance-change_buffering.html

大致意思如下

在 insert update 和 delete操作 如果涉及到索引列 特别是 secondary keys 的时候;如果所涉及到的数据不在buffer pool的时候,由于涉及到的数据 是无序的 那么 频繁的操作会导致大量的IO消耗。到数据被载入到buffer pool的时候,change buffer 会将数据合并在一起 然后更新到磁盘文件中。在空闲的时候,InnoDB的主线程 会合并buffer changes的数据。

由于change buffer 占用了部分buffer pool,降低了内存中可以缓存的数据页,如果数据已经载入到buffer pool或者有很少的secondary indexs,最好禁用掉这个特性。

可设置的值如下

  • all

    The default value: buffer inserts, delete-marking operations, and purges.
  • none

    Do not buffer any operations.
  • inserts

    Buffer insert operations.
  • deletes

    Buffer delete-marking operations.
  • changes

    Buffer both inserts and delete-marking operations.
  • purges

    Buffer the physical deletion operations that happen in the background.

Undo log

Undo log是InnoDB MVCC事务特性的重要组成部分,记录的是老版本的数据。主要作用是回滚数据,也可以根据undo log回溯到某个特别的版本的数据,实现MVCC。undo数据会首先被刷新到undo buffer中 之后在合适的时间 undo buffer中的数据 会被刷新到磁盘中,所有的undo log 会存放在ibd数据文件中(表空间)。Innodb 中存在purge线程,他们会查询那些无人问津的旧版本数据或者也内容标记为删除的操作也会被清理掉,从而保证undo log 不至于无限增长。

内存管理算法

通常,数据库中的缓冲池是通过LRU即最近最少使用算法来管理,最少使用的也在LRU列表的尾端,当缓冲池中的数据满了之后,会首先释放LRU列表中的尾端的页数据。

但是InnoDBd的存储引擎在读取到新的数据页的时候,不是直接放到LRU列表的首部,而是根据midpoint位置。这样的好处是如果是扫描数据的时候,通常需要操作许多的数据页,而这些数据又仅仅是在这次查询中需要,并不属于活跃的热数据。防止将最活跃的数据被移动到LRU尾部而被释放。

InnoDB体系架构总结(二)的更多相关文章

  1. InnoDB体系架构(二)内存

    InnoDB体系架构(二)内存 上篇文章 InnoDB体系架构(一)后台线程 介绍了MySQL InnoDB存储引擎后台线程:Master Thread.IO Thread.Purge Thread. ...

  2. InnoDB体系架构(三)Checkpoint技术

    Checkpoint技术 前篇 InnoDB体系架构(二)内存 从缓冲池.缓冲池的管理.重做日志缓冲.额外内存缓冲这四个点介绍了InnoDB存储引擎的内存结构,而在将缓冲池的数据刷新到磁盘的过程中使用 ...

  3. InnoDB体系架构(四)Master Thread工作方式

    Master Thread工作方式 在前面的文章:InnoDB体系架构——后台线程 说到:InnoDB存储引擎的主要工作都是在一个单独的后台线程Master Thread中完成.这篇具体介绍该线程的具 ...

  4. InnoDB体系架构(一)后台线程

    InnoDB体系架构——后台线程 上一篇已经了解了MySQL数据库的体系结构 这一篇除了介绍InnoDB存储引擎的体系架构外,同时进一步了解InnoDB的后台线程. InnoDB存储引擎是多线程的模型 ...

  5. InnoDB体系架构

    MySQL支持插件式存储引擎,常用的存储引擎则是MyISAM和InnoDB,通常在OLTP(Online Transaction Processing 在线事务处理)中,我们选择使用InnoDB,所以 ...

  6. 2.3 InnoDB 体系架构

    下图简单显示了InnoDB的存储引擎的体系架构,从图可见,InnoDB储存引擎有多个内存块,可以认为这些内存块组成了一个大的内存池,负责如下工作: 维护所有进程/线程需要访问的多个内部数据结构 缓存磁 ...

  7. F2工作流引擎这工作流引擎体系架构(二)

    F2工作流体系架构概览图 为了能更好的了解F2工作流引擎的架构体系,花了些时间画了整个架构的体系图.F2工作流引擎遵循参考WFCM规范,目标是实现轻量级的工作流引擎,支持多种数据库及快速应用到任何基于 ...

  8. InnoDB体系架构总结(一)

    缓冲池:    是一块内存区域,通过内存的速度来弥补磁盘速度较慢对数据库性能的影响.在数据库中读取的页数据会存放到缓冲池中,下次再读取相同页的时候,会首先判断该页是否在缓冲池中.对于数据库中页的修改操 ...

  9. MySQL技术内幕 InnoDB存储引擎 之 InnoDB体系架构

    后台线程 1.Master Thread 2.IO Thread 3.Purge Thread 4.Page Cleaner Thread  内存 重做日志在以下三种情况下将重做日志缓存中的内容刷新到 ...

随机推荐

  1. [題解]luogu_P1052 過河

    來源:題解 不發題面 因為 l 範圍太大,而石子數卻很少,步數也僅僅在1~10之間, 也就是說兩個石子之間很有可能間隔很大的距離,不管怎麼跳都能跳過去,那麼中間那些怎麼樣都能跳過去的區間和沒有等價, ...

  2. jmeter beanshell处理请求响应结果时Unicode编码转为中文

    在Test Plan下创建一个后置BeanShell PostProcessor,粘贴如下代码即可: String s=new String(prev.getResponseData()," ...

  3. urllib库的基本使用

    urllib库的使用 官方文档地址:https://docs.python.org/3/library/urllib.html 什么是urllib Urllib是python内置的HTTP请求库包括以 ...

  4. python 基础(四) 函数

    函数 一.什么是函数? 函数是可以实现一些特定功能的 小方法 或者是小程序 优点: 提高 了代码的后期维护 增加了代码的重复使用率 减少了代码量 提高了代码可读性 二.函数的定义 使用 def关键+函 ...

  5. Hive_Hive的管理_远程服务

    远程服务启动方式 - 端口号10000 - 启动方式: #hive --service hiveserver & 以JDBC或ODBC的程序登陆到hive中操作数据时,必须选用远程服务启动方式 ...

  6. morphia(2)-添加

    1.简单 @Test public void add() throws Exception { final Employee em = new Employee("遥远2",500 ...

  7. 调用submit()方式提交表单

    今天在看高级程序设计时看到的这样一段话: 在以调用submit()方法的形式提交表单时,不会触发submit事件 写了一个小例子做了下测试,的确如此: <form id="fm&quo ...

  8. yii2 操作数据库

    1.查询 User::find()->all(); 此方法返回所有数据: User::findOne($id); 此方法返回 主键 id=1 的一条数据(举个例子): User::find()- ...

  9. hihocoder1776 序列

    思路: 考虑从左至右依次向每个位置放置数字,对于第i个位置,以i为结尾的i个前缀和模P是不能相等的(因为不存在和为P的倍数的子串),所以第i个位置只能放置P - i个不同的数字.则答案就是(P - 1 ...

  10. uvm_factory——我们的工厂(二)

    上节我们说到uvm_object_registry #(T),uvm_object_reistry 又继承自uvm_object_wrapper,所以首先,让我们先看看它爹是啥样子的: //----- ...