元数据解决分表不可 mysql_MySQL InnoDB技术内幕:内存管理、事务和锁

前面有多篇文章介绍过MySQL InnoDB的相关知识,今天我们要更深入一些,看看它们的内部原理和机制是如何实现的。

一、内存管理

我们知道,MySQl是一个存储系统,数据最后都写在磁盘上。我们以前也提到过,磁盘的速度特别是大容量的磁盘受磁头臂的影响,速度相对内存慢很多。所以Innodb实现了自己的缓存机制。

首先我们先看下Innodb对内存是如何使用和划分的,然后我们再看看它是如何保存热数据的。

1、主要模块和组成

(1) Buffer Pool

预分配的内存池

(2) Page

Buffer Pool的最小单位

(3) Free list

空闲Page组成的链表

(4) Flush list

脏页链表

(5) Page hash 表

维护内存Page和文件Page的映射关系

(6) LRU

内存淘汰算法

以上三种链表LRU list、Free list、Flush list 和内存池、Page hash 以及磁盘文件之间的映射关系如下图所示:

06821852ed1c11d6e039c31a2e6eb009.png

2、LRU算法

LRU,Least Recent Used,最近最少使用。每次将刚使用过的页面插到LRU队列的最前端,那么最少使用的排在尾端,当缓存不够时,淘汰尾端的页。

很多文件系统和开源库的内存淘汰算法都用到了LRU,以前有不少文章都提到过。

但是LRU的缺陷是,有时会无法淘汰真正的冷数据,尾端的数据可能暂时没使用而已,不代表不使用频繁,不代表不是热数据。所以很多系统对LRU进行了优化。

比如Redis加了LFU(least frequently used最不经常使用)配合LRU一起使用。

那么InnoDB存储引擎是如何改进的呢?如下图,它将LRU分成两部分,中间的分割点叫做midpoint,新读取的页不再是加入到最头部,而是midpoint后面的位置,即后半截的头部。

那么midpoint的位置是如何计算的呢,在默认配置下,离LRU整个头部的5/8处。当然这个比例是可以根据实际业务进行设置的。但总之,可以真正将 冷热数据分离 ,热数据在前,冷数据在后。

a3d832714f9f43d13ff9fe338d42963a.png

那么这两个区的数据如何移动的呢,即冷热数据如何切换的呢?

上面我们提到了,刚插入的页放在old区的头部,那么如果该页确实访问频繁,不能一直呆在该位置吧。

InnoDB引入了参数innodb_old_blocks_time,如果old区的数据在该时间范围内没有被淘汰出去,就可以移到new区,加入到new区的头部。这也叫做 made young 。

而如果在old呆的时间不够innodb_old_blocks_time,而且缓存不够时,就会面临直接淘汰,这就叫做 made not young 。这种情况,可以发生在全表扫描的时候,保证了new区的数据才是真正的热数据!

当然数据也有可能从 new区移动到old区 ,只是相对比较简单了,直接移动midpoint指向的位置即可。即new区的尾巴变成了old区的头部。

二、事务

1、 MySQL事务基本概念

事务特性

A(Atomicity原子性):全部成功或全部失败

I(Isolation隔离性):并行事务之间互不干扰

D(Durability持久性):事务提交后,永久生效

C(Consistency一致性):通过AID保证

并发问题

脏读(Drity Read):读取到未提交的数据。 中间所有变化的值都可能读到 。

不可重复读(Non-repeatable read):两次读取结果不同。读取已提交的(不一样的值), 读到的值变化数量比脏读要少 。

幻读(Phantom Read):select 操作得到的结果所表征的数据状态 影响(无法支撑)后续的业务操作。

网上有人这样区分,脏读是读取修改的数据,幻读是读取新提交的数据。我认为也可以,或许phantom表示 虚幻的新数据 (所以无法支撑后续操作),而drity代表了修改的意思呢?

所以,不可重复读重点在于update和delete,而幻读的重点在于insert。

隔离级别

Read Uncommitted(读取未提交内容):最低隔离级别,会读取到其他事务未提交的数据;存在 脏读 的问题 。

Read Committed(读取提交内容):事务过程中可以读取到其他事务已提交的数据;存在 不可重复读 的问题 。

Repeatable Read(可重复读):每次读取相同结果集,不管其他事务是否提交;存在 幻读 的问题 。

Serializable(串行化):事务排队,隔离级别最高,性能最差。

2、MySQL事务实现原理

从上我们可以看出事务有ACID四大特性,而“I”隔离性是通过锁来实现的,我们下一节讲述。那么其他三个特性主要通过undo/redo日志的机制来实现,这个知识点在前面有一篇文章中介绍和对比过。 现在我们站在事务实现的角度再来看看。

(1)undo log

回滚日志,顾名思义,是对事物rollback时使用。这是它核心的功能之一,但是它还有另一个非常重要的功能,MVCC。所以今天这里主要介绍它是如何在事务中发挥作用的。

MVCC

Multiversion concurrency control,多版本并发控制。当用户读取一行时,如果该记录已经被其他事务占用,当前事务可以通过undo读取之前的 行版本信息 ( 快照数据 ),以此实现 非锁定读 。 所以实现了非阻塞的读操作,写操作也只锁定必要的行。即 解决读-写冲突。

快照数据就是当前行数据的历史版本,每行记录可能含有多个版本。那该读取哪个版本呢?

首先,InnoDB的每行记录或者说每条数据,除了记录用户定义的列之外,还有 两个隐藏的列 :事务ID列 DB_TRX_ID 和回滚指针 DB_ROLL_PTR。 如果该表没有定义主键,每行还会增加一个rowid列。 DB_TRX_ID是当时执行这条sql的事务id,DB_ROLL_PTR指向的就是undo log中修改前的行DB_ROW_ID。所以对同一条数据的修改,通过roll_pointer就形成了 undo log版本链 。

f5fefedbceab3dc70e3bb794718e8040.png

然后我们再来介绍下 Read View 快照读。

一般情况下读取数据时会生成一个Read View,对当前该行的可能正在进行的事务进行一个快照。

Read View中主要包含4个比较重要的内容:

m_ids:表示在生成Read View时当前系统中活跃的读写事务的事务id列表,简称 活跃列表 。

min_trx_id:表示在生成Read View时当前系统中活跃的读写事务中 最小的事务id ,也就是m_ids中的最小值。

max_trx_id:表示生成Read View时系统中应该分配给下一个事务的 id 值,,也就是m_ids中的 最大 值。

creator_trx_id:表示生成该Read View的事务的事务id。

b225daee490b6787a8d1e5c27f1d92e6.png

有了这些信息,这样在访问某条记录时,只需要依次判断undo log版本链中节点的事务ID 是否可见 ,如果可见即找到了所需要的行记录。

77327534fe53a49ff9ca5c06d7b61e60.png

最后,READ COMMITTED和REPEATABLE READ两种隔离级别对于快照数据生成的时机不一样。

对于RC,在每次查询语句执行的过程中,都关闭Read View, 再创建当前的一份Read View。这样就会产生不可重复读现象。

对于RR,创建事务trx结构的时候,就生成了当前的global Read View,一直维持到事务结束。在事务结束这段时间内每一次查询都不会重新重建Read View,从而实现了可重复读。

undo log分为两种格式 ,处理不一样。

insert undo log:用于回滚,提交即清理;不需要进行purge操作。

update undo log:用于回滚,同时实现快照读,不能随便删除,所以需要等待purge线程来判断何时删除。它记录的是对delete和update的操作产生的undo log。

注:以上来自书上的说法,网上有人把第一种说成delete undo log,包括insert和delete操作,供参考。

还需要补充一点的是,update undo log怎样去清理, 应该是根据系统活跃的Read view中最小的活跃事务ID之前的即可清除。

(2)redo log

redo日志其实在《 MySQL的undo/redo日志和binlog日志,以及2PC 》文章中介绍比较多,也提到了XA事务的2PC。我们这里简单介绍下普通事务的流程。

d8660011ec96b54e943247a962105918.png

写入流程仍然可以分为两步,类似二阶段提交:

记录页的修改,状态为prepare

事务提交,讲事务记录为commit状态

三、锁

1、InnoDB锁种类

(1) 类型

共享锁(S)

读锁,可以同时被多个事务获取,阻止其他事务对记录的修改。

排他锁(X)

写锁,只能被一个事务获取,允许获得锁的事务修改数据。

而读其实又可以分为 当前读 (锁定读)和快照读(非锁定读),而快照读通过上一节描述的MVCC来实现。

当前读, 读取的是最新版本,所以需要对读取的记录加锁,阻塞其他事务改动该记录。当前读又分为两种方式:

select...for update,对读取的行加X锁;

select...lock in share mode ,对读取的行加S锁。

(2)锁粒度

行级锁

Record Lock,单个记录上的锁。

锁直接加在索引记录上面,锁住的是key,所以必须是 聚簇索引或者二级索引是唯一索引。

间隙锁

Gap Lock,间隙锁,锁定一个范围,单不包含记录本身。

InnoDB存储引擎的隔离级别默认是Repeatable Read,所以引入了间隙锁 解决 可重复读模式下的 幻读问题 。

GAP锁不是加在记录上, 锁住的位置是两条记录之间的GAP; 保证 两次当前读 返回一致的记录。

所以两次当前读之前,其他的事务 不会插入新的 满足条件的记录。

我们来整理下着两者的关系和区别。

Record Lock针对的是索引必须具备唯一性;而GAP锁针对的是索引不具备唯一性但需要保证可重复读,也就是说如果发现数据有被其他事务修改的可能,那就把前后间隙都加上锁。

比如说如下图,有个用户表,uid为主键,那么就只需要103这条记录加上行锁即可。

85517411472e55490a6ba111c15e6af6.png

但是如果我们变化下查询条件(phone列上建立了二级索引),则除了对于这两条记录加锁外,对前后的间隙也需要加锁。当然这种情况是针对RR的隔离级别,如果隔离级别是RC或者更低,安全性就没有这么高,系统会自动降级到行锁。

caf1604c1bac739750e049551820708a.png

Next-Key Lock,是Record Lock与Gap Lock的一个结合。理解了上述两种锁的原理,对于它而言就很容易了。

表级锁

Table Lock,锁定整张表。

主要用在运维的时候,对表格进行操作比如MDL或者元数据的操作 (meta data lock)等等。

当然有些情况下会触发锁升级: 全表扫描。全表扫描的触发一般情况下是当前被查询的字段没有建立任何索引。

而表级锁事实上是对所有记录和所有的间隙都加上锁。

所以全表扫描的效率非常低,要尽量避免。

2、InnoDB加锁过程

如下图,当我们更新多条数据时,是一行一行的加锁。

843929bc79bc3c7c27071e61add15c3a.png

所以当同时出现对多条记录交叉查询时,很容易出现AB-BA死锁,如下图操作。

f07aa9d5c2497c80822cfb5c5d1fb38b.png

附录

分库分表的建议:

是否分表

建议单表不超过1KW

分表方式

取模:存储均匀&访问均匀

按时间:冷热库

分库

按业务垂直分

水平查分多个库

参考:

《MySQL技术内幕InnoDB存储引擎》

内部培训资料

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/530322.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

navicat for mysql 13_Navicat for MySQL下载

Navicat for MySQL 是一套管理和开发 MySQL 或 MariaDB 的理想解决方案。它使你以单一程序同时连接到 MySQL 和 MariaDB。这个功能齐备的前端软件为数据库管理、开发和维护提供了直观而强大的图形界面。它提供了一组全面的工具给 MySQL 或MariaDB 新手,同时给专业人…

spring兼容mysql_springboot 最新版本支持 mysql6.0.6吗

缥缈止盈1.首先在pom文件中加入下列依赖,一个使用jpa所需依赖,一个连接MySQL使用的依赖:mysqlmysql-connector-javaorg.springframework.bootspring-boot-starter-data-jpa 123456789102.在配置文件中添加datasource配置和jpa配置,在mysql中已经提前创建了一个名为db_test的数据…

java虚拟机内存监控_java虚拟机内存监控工具jps,jinfo,Jstack,jstat,jmap,jhat使用...

将会打印出很多jvm运行时参数信息,由于比较长这里不再打印出来,可以自己试试,内容一目了然Jstack(Stack Trace for Java):JVM堆栈跟踪工具jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息,如…

idea 调试java技巧_IDEA 调试Java代码的两个技巧

本文介绍两个使用IDEA 调试Java代码的两个技巧:修改变量值使用RuntimeException终止代码执行修改变量值在Java代码调试过程中,我们可以修改变量值,使其达到走指定分支的目的,或者使其满足某个条件。我们以给变量beanName赋值为例&…

java免检异常_java-异常

java提供了异常处理机制:程序运行受阻时候的处理方式。1、异常分类Error:系统错误,由java虚拟机抛出,很少发生;免检异常RuntimeException:程序设计错误,通常由java虚拟机抛出;免检异…

java编程需要数学知识吗_初学Java编程,需要英语和数学基础吗?

原标题:初学Java编程,需要英语和数学基础吗?“学习Java编程英语和数学是必备条件吗?”很多Java零基础学习或者转型IT行业的都会有这样的疑问,其实刚开始学习Java编程是不需要太高深的数学和英语基础的。刚开始学习Java…

java简单数据结构_图解Java常用数据结构

最近在整理数据结构方面的知识, 系统化看了下 Java 中常用数据结构, 突发奇想用动画来绘制数据流转过程.主要基于 jdk8, 可能会有些特性与 jdk7 之前不相同, 例如 LinkedList LinkedHashMap 中的双向列表不再是回环的.HashMap 中的单链表是尾插, 而不是头插入等等, 后文不再赘叙…

java const关键字_const关键字:终于拥有真正的常量声明语句

你好,今天大叔想和你唠扯唠扯 ES6 新增的关键字 —— const。在说 const 关键字之前,大叔先和你唠唠大叔自己对 const 的感受 —— JavaScript 尼玛终于可以声明真正的常量啦!大叔为啥会发出这样滴感叹?实在是“天下苦秦久矣”呀~…

workerman高并发异步mysql_workerman怎么实现高并发

并发概念太模糊,这里以两种可以量化的指标并发连接数和并发请求数来说明。并发连接数是指服务器当前时刻一共维持了多少TCP连接,而这些连接上是否有数据通讯并不关注。 (推荐学习: workerman教程)例如一台消息推送服务器上可能维持了百万的设…

checkout 撤销修改_Git的4个阶段的撤销更改

虽然git诞生距今已有12年之久,网上各种关于git的介绍文章数不胜数,但是依然有很多人(包括我自己在内)对于它的功能不能完全掌握。以下的介绍只是基于我个人对于git的理解,并且可能生编硬造了一些不完全符合git说法的词语。目的只是为了让git通…

java访问权限最高_java 访问权限

Java语言中的访问权限修饰符有4种,但是仅有3个关键字,因为不写访问权限,在Java中被称为默认权限,或同包权限,本文中以(default)代替。下面按照权限从小到大的顺序对4中访问权限分别介绍。class我个人,我有很…

java中fork函数_java中的forkjoin框架的使用

fork join框架是java 7中引入框架,这个框架的引入主要是为了提升并行计算的能力。fork join主要有两个步骤,第一就是fork,将一个大任务分成很多个小任务,第二就是join,将第一个任务的结果join起来,生成最后…

java中已定义类型car_Java 8 习惯用语(8):Java 知道您的类型

Java™8是第一个支持类型推断的 Java 版本,而且它仅对 lambda 表达式支持此功能。在 lambda表达式中使用类型推断具有强大的作用,它将帮助您做好准备以应对未来的 Java版本,在今后的版本中还会将类型推断用于变量等更多可能。这里的诀窍在于恰…

java web 来源页_Java:Java Web--分页效果

先来看一看分页的实现原理万能公式.jpg项目目录.PNG首先,新建Java Web项目一. 梳理业务逻辑重定向到URL(跳转到StudentViewAction页面)//index.jsp页面1.从页面接收可变的值2.接收值有问题时,初始化为13.如果没有问题,把String类型接收值强转成Integer4.实例DAO方法,调用findSt…

java 浏览器 安全_安全策略-IE浏览器防黑十大秘籍

1.管理好Cookie在IE6.0中,打开“工具”→“Internet选项”→“隐私”对话框,这里设定了“阻止所有Cookie”、“高”、“中高”、“中”、“低”、“接受所有Cookie”六个级别,你只要拖动滑块就可以方便地进行设定,而点击下方的“编…

php截取指定字符串之后,php截取字符串(截取指定字符串之间的字符串)

一、PHP截取两个指定字符后边的字符$a "123abc#456";$b (strpos($a,""));$c (strpos($a,"#"));echo substr($a,$b1,$c-1);二、常用截取字符串技巧。//构造字符串$str "ABCDEFGHIJKLMNOPQRSTUVWXYZ";echo "原字符串:…

php 获取key的位置,PHP获取当前所在目录位置的方法

本文实例讲述了PHP获取当前所在目录位置的方法。分享给大家供大家参考。具体分析如下:如果要获取脚本文件的目录,要应用函数getcwd()来实现。函数声明如下:string getcwd ( void ) ;成功执行后返回当前目录字符串,失败返回FALSE。…

php连接数据库navicat,navicat数据库如何连接php

第一步,打开Navicat,新建数据库。第二步,在数据库中新建表。相关推荐:《Navicat for mysql使用图文教程》第三步,保存表。第四步,表中添加数据。第五步,打开ide,输入以下php代码&…

每日一题:LCR 095.最长公共子序列(DP)

题目描述: 给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。 一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些…

php自动合并,php实现合并数组并去除重复的方法

php实现合并数组并去除重复的方法发布时间:2020-08-12 10:35:05来源:亿速云阅读:99作者:小新这篇文章主要介绍了php实现合并数组并去除重复的方法,具有一定借鉴价值,需要的朋友可以参考下。希望大家阅读完这…