mysqlinnodb(mysqlinnodb引擎索引的常用数据结构)
本篇文章给大家谈谈mysqlinnodb,以及mysqlinnodb引擎索引的常用数据结构对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。
本文目录一览:
MYSQL存储引擎InnoDB(三十五):临时表空间
InnoDB使用会话临时表空间和全局临时表空间。
在InnoDB配置为磁盘内部临时表的存储引擎时,会话临时表空间存储用户创建的临时表和优化器创建的内部临时表。从 MySQL 8.0.16 开始,用于磁盘内部临时表的存储引擎固定为InnoDB。(之前,存储引擎由internal_tmp_disk_storage_engine的值决定 )
在第一次请求创建磁盘临时表时会话临时表空间从临时表空间池中被分配给会话。一个会话最多分配两个表空间,一个用于用户创建的临时表,另一个用于优化器创建的内部临时表。分配给会话的临时表空间用于会话创建的所有磁盘临时表。当会话断开连接时,其临时表空间将被截断并释放回池中。服务器启动时会创建一个包含 10 个临时表空间的池。池的大搏链小永远不会缩小,并且表空间会根据需要搭键自动添加到池中。临时表空间池在正常关闭或中止初始化时被删除。会话临时表空间文件在创建时大小为 5 页,并且具有.ibt文件扩展名。
InnoDB为会话临时表空间保留了40 万个空间 ID。因为每次启动服务器时都会重新创建会话临时表空间池,所以会话临时表空间的空间 ID 在服务器关闭时不会保留,并且可以重复使用。
innodb_temp_tablespaces_dir 变量定义了创建会话临时表空间的位置。默认位置是 #innodb_temp数据目录中的目录。如果无法创建临时表空间池,则会拒绝启动。
在基于语句的复制 (SBR) 模式下,在副本上创建的临时表驻留在单个会话临时表空间中,该临时表空间仅在 MySQL 服务器关闭时被截断。
INNODB_SESSION_TEMP_TABLESPACES 表提供有关会话临时表空间的元数据。
该INFORMATION_SCHEMA.INNODB_TEMP_TABLE_INFO表提供有关在InnoDB实例中处于活动状态知银巧的用户创建的临时表的元数据。
全局临时表空间 ( ibtmp1) 存储对用户创建的临时表所做的更改的回滚段。
innodb_temp_data_file_path 变量定义了全局临时表空间数据文件的相对路径、名称、大小和属性。如果没有为innodb_temp_data_file_path指定值 ,则默认行为是创建innodb_data_home_dir目录中命名为ibtmp1的单个自动扩展数据文件。初始文件大小略大于 12MB。
全局临时表空间在正常关闭或中止初始化时被删除,并在每次服务器启动时重新创建。全局临时表空间在创建时会收到一个动态生成的空间 ID。如果无法创建全局临时表空间,则拒绝启动。如果服务器意外停止,则不会删除全局临时表空间。在这种情况下,数据库管理员可以手动删除全局临时表空间或重新启动 MySQL 服务器。重新启动 MySQL 服务器会自动删除并重新创建全局临时表空间。
全局临时表空间不能驻留在原始设备上。
INFORMATION_SCHEMA.FILES提供有关全局临时表空间的元数据。发出与此类似的查询以查看全局临时表空间元数据:
默认情况下,全局临时表空间数据文件会自动扩展并根据需要增加大小。
要确定全局临时表空间数据文件是否正在自动扩展,请检查以下 innodb_temp_data_file_path 设置:
要检查全局临时表空间数据文件的大小,请使用与此类似的查询来查询INFORMATION_SCHEMA.FILES表:
TotalSizeBytes显示全局临时表空间数据文件的当前大小。
或者,检查操作系统上的全局临时表空间数据文件大小。全局临时表空间数据文件位于 innodb_temp_data_file_path 变量定义的目录中。
要回收全局临时表空间数据文件占用的磁盘空间,请重新启动 MySQL 服务器。重新启动服务器会根据innodb_temp_data_file_path定义的属性删除并重新创建全局临时表空间数据文件 。
要限制全局临时表空间数据文件的大小,请配置 innodb_temp_data_file_path以指定最大文件大小。例如:
配置 innodb_temp_data_file_path 需要重新启动服务器。
[img]Mysql InnoDB索引原理
理解Mysql索引的原理和数据结构有助于我们更好的使用索引以及进行SQL优化,索引是在存储引擎层面实现的,所以不同的引擎实现的索引也有一定的区别,但是在生产环境中,我们最常用的就是InnoDB引擎和B树索引,OK,那本文要讨论的重点也同样是 InnoDB引擎下的B树索引 。
我们建立一个表来进行测试,表的DDL如下所示,我们要关注的是表t_book上的主键索引id和name author publish_date三列组成的索引test_index。
Mysql中的B树索引是使用B+树实现的,关于B+树的数据结构个人认为美团点评技术博客中Mysql索引原理及慢查询优化一拆团缓文中介绍的非常详实,B+树的数据结构如下图所示。
图中浅蓝色块即磁盘块,根节点磁盘块中存储17和35两个数据,其中指针P1指向小于17的数据,指针P2指向大于17小于35的数据,指针P3指向大于35的数据。显然通过B+树索引查询数据与B+树的高度有关,如上图的B+树索引查找一个叶子节点的数据只需要三次磁盘IO,对于Mysql来说三层的B+树可以索引上百万的数据,这对于查询效率的提升是巨大的。
总结起来Mysql中B树索引有以下关键特点:
Mysql中的B树索引有两种数据存储形式,一种为聚簇索引,一种为二级索引。
InnoDB一般会使用表的主键来作为聚簇索引,如果一个表没有主键(不建议这么玩)InnoDB会选用一个唯一非空索引来代替,如果没有这样的索引,InnoDB会隐式建立一个聚簇索引。聚簇的含义即是数据行和相邻的键值紧凑的存储在一起,占据一块连续的磁盘空间,因此通过聚簇索引访问数据可以有效减少随机IO,通常使用聚簇索引查找比非聚簇索引查找速度更快。以我们建立的表t_book为例,聚簇索引即为自增主键id,其B树索引数据结构可以用下图来表示。
聚簇索引有以下关键特点:
InnoDB的B树索引中除了聚簇索引,就都是二级索引了,二级索引的含义是索引的叶子节点除了存储了索引值,还存储了主键id,在使用二级索引进行查询时,查找到二级索引B树上的叶子节点后还需要去聚簇索引上去查询真实数据,但是这里有一种特殊情况,即查询所需的所有字段在二级索引中都可以获取,此时就不需要再去回表查数据了,这种情况就是索引覆盖(EXPLAIN中EXTRA列中会出现USING INDEX,本文只关注或简索引结构,不详细讨论索引覆盖等技术的使用,旅模如果深入理解索引的数据结构,索引覆盖等技术也没有那么神秘)。
在我们的测试表t_book中,test_index即为二级索引,由于我们把除了主键id所有的列都作为一个联合索引,所以在这个表上的查询都可以使用索引覆盖技术,但是具体生产环境中也不建议总是采用这种做法,索引列的增加也会增大插入更新数据时的索引更新成本,具体的优化要视具体情况决策。t_book上的二级索引test_index的索引结构由下图表示。
通过以上结构,我们可以推断出二级索引的以下关键特点:
索引覆盖:
最左前缀匹配:
二级索引可以说是我们在Mysql中最常用的索引,通过理解二级索引的索引结构可以更容易理解二级索引的特性和使用。
最后聊点轻松的索引结构,哈希索引就是通过哈希表实现的索引,即通过被索引的列计算出哈希值,并指向被索引的记录。
哈希索引有如下特性:
Mysql索引原理及慢查询优化
高性能Mysql 第三版
MySQL中innodb的行锁算法
众所周知,innodb是默认行锁,当然也支持表锁。如下是对于行锁的算法进行的一些实验。
锁的算法为:我知道是行锁,但是是如何锁的,锁多少数据
假如有个索引是:[1,2,3,7]
record lock 锁的是 1,2,3,7
gap lock 锁的是 (- ,1),(2,3),(3,7),(7,+ )反正锁的就是区间,不是行
next-key lock锁的是 (- ,1],[2,3),[3,7),[ 7,+ )既锁范围也锁行
Innodb锁算法规则如下:
在可重复读隔离级别下,innodb默认使用的是next-key lock算法,当查询的索引是主键或者唯一索引的情况下,才会退化为record lock,在使用next-key lock算法时,不仅仅会锁住范围,还会给范围最后的一个键值加一个gap lock。
其盯孙氏中lockmode中的X锁为左边会话中的锁,因为需要显式的commit之后才会释放锁,第二个S锁,为右边的共享锁,因为主键ID为1的已经被锁住了,所以处于锁等待状态,锁的类型为record lock
使用辅助索引a=8进行操作,这个时候理论应该对主键索引加record lock 则 主键ID=8的被锁,然后辅助索引被加next-key lock 则为:
(7,8] 然后对下一个键值加gap锁,则为:(8,11)
所以目前被锁住的记录为:
1.主键为凯槐8的被锁
2.辅助索引8的被锁
3.辅助索引8到11之间的被锁,意味着你这个时候往8到11之间写数据会报错
当使用凯散范围条件进行更新时,此时肯定是需要加X锁的,我是用的也是主键,所以按照理论应该是加的record lock ,但是却加了gap lock,因为插入值为10的阻塞了,查看information 也提示X.GAP
这个有点晕为啥主键变成了next-key lock ,不应该是record lock么?
update20200515
在知乎看到的一个解释:
即,在无论使用主键索引还是非主键索引的时候,请求共享锁或者排他锁,innodb会给范围内的记录加锁,而范围内的间隙也会被加锁,
例如一个表t 的 id为1,2,3,7,10
假如执行如下:
select * from t where id =3 for update
那么这个时候执行
insert into t(id) values(8) 会被阻塞,因为是在请求排他锁时使用了范围,所以[3,10],甚至10以后的任何数据都无法插入。
执行
select * from t where id =3 lock in share mode
insert into t(id) values(8) 会被阻塞,因为是在请求共享锁时使用了范围,所以[3,10],甚至10以后的任何数据都无法插入。
幻读是同一事务下,连续执行两次同样的sql可能导致不同的结果,第二次返回的数据可能导致以前不存在的行。
同时一般会问它和脏读的区别,脏读为读取到其他事务未提交的数据,但是幻读是读取的其他事务已经提交的数据。
reference:
MySQL innodb引擎深入讲解
表空间(ibd文件),一个MySQL实例可以对应多个表空间,用于存储记录,索引等数据。
段,分为数据段、索引段、回滚段,innodb是索引组织表,数据段就是B+Tree的叶子节点,索引段为非叶姿厅滑子节点,段用来管理多个区。
区,表空间的单元结构,每个区的大小为1M,默认情况下,innodb存储引擎页大小为16K,即一个区中一共有64个连续的页。
页,是innodb存储引擎磁盘管理的最小单元,每个页的大小为16K,为了保证页的连续性,innodb存储引擎每次从磁盘申请4~5个区。
行,innodb存储引擎数据是按行进行存储的。Trx_id 最后一次事务操作的id、roll_pointer滚动指针。
i nnodb的内存结构 ,由Buffer Pool、Change Buffer和Log Buffer组成。
Buffer Pool : 缓冲池是主内存中的一个区域,里面可以缓存磁盘上经常操作的真实数据,在执行增删改查操作时,先操作缓冲池中的数据(若缓冲池么有数据,则从磁盘加载并缓存),然后再以一定频率刷新磁盘,从而减少磁盘IO,加快处理速度。
缓冲池以迹腊page页为单位,底层采用链表数据结构管理page,根据状态,将page分为三种类型:
1、free page 即空闲page,未被使用。
2、clean page 被使用page,数据没有被修改过。
3、dirty page 脏页,被使用page,数据被修改过,这个page当中的数据和磁盘当中的数据 不一致。说得简单点就是缓冲池中的数据改了,磁盘中的没改,因为还没刷写到磁盘。
Change Buffer :更改缓冲区(针对于非唯一二级索引页),在执行DML语句时,如果这些数据page没有在Buffer Pool中,不会直接操作磁盘,而会将数据变更存在更改缓冲区Change Buffer中,在未来数据被读取时。再将数据合并恢复到Buffer Pool中,再将合并后的数据刷新到磁盘中。
二级索引通常是非唯一的,并且以相对随机的顺序插入二级索引页,同样,删除和更新可能会影响索引树中不相邻的二级索引页。如果每一次都操作磁盘,会造成大量磁盘IO,有了Change Buffer之后,我们可以在缓冲池中进行合并处理,减少磁盘IO。
Adaptive Hash Index: 自适应hash索引,用于优化对Buffer Pool数据的查询,InnoDB存储引擎会监控对表上各索引页的查询,如果观察到hash索引可以提升速度,则建立hash索引,称之为自适应hash索引。无需人工干预,系统根据情况自动完成。
参数:innodb_adaptive_hash_index
Log Buffer: 日志缓冲区,用来保存要写入到磁盘中的log日志数据(redo log、undo log),默认大小为16M,日志缓冲区的日志会定期刷新到磁盘中,如果需要更新,插入或删除许多行的事务,增加日志缓冲区的大小可以节省磁盘IO。
参数: innodb_log_buffer_size 缓冲区大小
innodb_flush_log_at_trx_commit 日志刷新到磁盘时机
innodb_flush_log_at_trx_commit=1 表示日志在每次事务提交时写入并刷新到磁盘
2 表示日志在每次事务提交后写入,并每秒刷新到磁盘一次
0 表示每秒将日志写入并刷新到磁盘一次。
InnoDB 的磁盘结构,由系统表空间(ibdata1),独立表空间(*.ibd),通用表空间,撤销表空间(undo tablespaces), 临时表空间(Temporary Tablespaces), 双写缓冲区(Doublewrite Buffer files), 重做日志(Redo Log).
系统表空间(ibdata1): 系统表空间是更改缓冲区的存储区域,如果表是在系统表空间而不是每个表文件或者通用表空间中创建的,它也可能包含表和索引数据。
参数为: innodb_data_file_path
独立表空间(*.ibd): 每个表的文件表空间伏陪包含单个innodb表的数据和索引,并存储在文件系 统上的单个数据文件中。 参数: innodb_file_per_table
通用表空间: 需要通过create tablespace 语法创建,创建表时 可以指定该表空间。
create tablespace xxx add datafile 'file_name' engine=engine_name
create table table_name .... tablespace xxx
撤销表空间(undo tablespaces): MySQL实例在初始化时会自动创建两个默认的undo表空间(初始大小16K,undo_001,undo_002),用于存储undo log 日志
临时表空间(Temporary Tablespaces): innodb使用会话临时表空和全局表空间,存储用 户创建的临时表等数据。
双写缓冲区(Doublewrite Buffer files): innodb引擎将数据页从Buffer Pool刷新到磁盘前,先将数据页写入缓冲区文件中,便于系统异常时恢复数据。
重做日志(Redo Log): 是用来实现事务的持久性,该日志文件由两部分组成,重做日志缓冲区(redo log buffer)以及重做日志文件(redo log),前者是在内存中,后者在磁盘中,当事务提交之后会把修改信息都会存储到该日志中,用于在刷新脏页到磁盘时,发送错误时,进行数据恢复使用。以循环方式写入重做日志文件,涉及两个文件ib_logfile0,ib_logfile1。
那内存结构中的数据是如何刷新到磁盘中的? 在MySQL中有4个线程负责刷新日志到磁盘。
1、Master Thread, mysql核心后台线程,负责调度其它线程,还负责将缓冲池中的数据异 步刷新到磁盘中,保持数据的一致性,还包括脏页的刷新,合并插入缓冲、undo页的回 收。
2、IO Thread,在innodb存储引擎中大量使用了AIO来处理IO请求,这样可以极大地提高数 据库的性能,而IO Thead主要负责这些IO请求的回调。
4个读线程 Read thread负责读操作
4个写线程write thread负责写操作
1个Log thread线程 负责将日志缓冲区刷新到磁盘
1个insert buffer线程 负责将写入缓冲区内容刷新到磁盘
3、Purge Thread,主要用于回收事务已经提交了的undo log,在事务提交之后,undo log 可能不用了,就用它来回收。
4、Page Cleaner Thread, 协助Master Thread 刷新脏页到磁盘的线程,它可以减轻主线程 的压力,减少阻塞。
事务就是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失效。
事务的4大特性分为:
如何保证事务的4大特性,原子性,一致性和持久性是由innodb存储引擎底层的两份日志来保证的,分别是redo log和undo log。对于隔离性是由锁机制和MVCC(多版本并发控制)来实现的。
redo log,称为重做日志,记录的是事务提交时数据页的物理修改,是用来实现事务的持久性。该日志文件由两部分组成: 重做日志缓冲redo log buffer及重做日志文件redo log file,前者是在内存中,后者是在磁盘中,当事务提交之后会把所有修改信息都存到该日志文件中,用于在刷新脏页到磁盘,发送错误时,进行数据的恢复使用,从而保证事务的持久性。
具体的操作流程是:
1、客户端发起事务操作,包含多条DML语句。首先去innodb中的buffer pool中的数据页去查找有没有我们要更新的这些数据,如果没有则通过后台线程从磁盘中加载到buffer pool对应的数据页中,然后就可以在缓冲池中进行数据操作了。
2、此时缓冲池中的数据页发生了变更,还没刷写到磁盘,这个数据页称为脏页。脏页不是实时刷新到磁盘的,而是根据你配置的刷写策略进行刷写到磁盘的(innodb_flush_log_at_trx_commit,0,1,2三个值)。如果脏页在往磁盘刷新的时候出现了故障,会丢失数据,导致事务的持久性得不到保证。为了避免这种现象,当对缓冲池中的数据进行增删改操作时,会把增删改记录到redo log buffer当中,redo log buffer会把数据页的物理变更持久化到磁盘文件中(ib_logfile0/ib_logfile1)。如果脏页刷新失败,就可以通过这两个日志文件进行恢复。
undo log,它是用来解决事务的原子性的,也称为回滚日志。用于记录数据被修改前的信息,作用包括:提供回滚和MVCC多版本并发控制。
undo log和redo log的记录物理日志不一样,它是逻辑日志。可以认为当delete一条记录时,undo log中会记录一条对应的insert记录,当update一条记录时,它记录一条对应相反的update记录,当执行rollback时,就可以从undo log中的逻辑记录读取到相应的内容并进行回滚。
undo log销毁: undo log 在事务执行时产生,事务提交时,并不会立即删除undo log,因为这些日子可能用于MVCC。
undo log存储: undo log 采用段的方式进行管理和记录,存放在前面介绍的rollback segment回滚段中,内部包含1024个undo log segment。
mvcc(multi-Version Concurrency Control),多版本并发控制,指维护一个数据的多个版本,使得读写操作没有冲突,快照读为MySQL实现MVCC提供了一个非阻塞读功能,MVCC的具体实现,还需要依赖于数据库记录中的三个隐式字段,undo log日志、readView。
read committed 每次select 都生成一个快照读
repeatable read 开启事务后第一个select语句才是快照读的地方
serializable 快照读会退化为当前读。
mvcc的实现原理
DB_TRX_ID: 最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID
DB_ROLL_PTR: 回滚指针,指向这条记录的上一个版本,用于配合undo log,指向上一个 版本
DB_ROW_ID: 隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段。
m_ids当前活跃的事务ID集合
min_trx_id: 最小活跃事务id
max_trx_id: 预分配事务ID,当前最大事务id+1,因为事务id是自增的
creator_trx_id: ReadView创建者的事务ID
版本链数据访问规则:
trx_id: 表示当前的事务ID
1、trx_id == creator_trx_id? 可以访问读版本--成立的话,说明数据是当前这个事务更改的
2、trx_id 成立,说明数据已经提交了。
3、trx_idmax_trx_id?不可用访问读版本- 成立的话,说明该事务是在ReadView生成后才开启的。
4、min_trx_id
关于mysqlinnodb和mysqlinnodb引擎索引的常用数据结构的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。