一文理解MySQL存储引擎、索引、事务及锁原理

tech2025-01-09  9

1、MySQL中的存储引擎

1.1、什么是存储引擎

mysql中建立的库===>文件夹

库中建立的表===>文件

​ 现实生活中我们用来存储数据的文件应该有不同的类型:比如存文本用txt类型,存表格用excel,存图片用png等。数据库中的表也应该有不同的类型,表的类型不同,会对应mysql不同的存取机制,表类型又称为存储引擎。即数据存放、管理的方式。

​ 存储引擎说白了就是如何存储数据、如何为存储的数据建立索引和如何更新、查询数据等技术的实现方法。因为在关系数据库中数据的存储是以表的形式存储的,所以存储引擎也可以称为表类型(即存储和操作此表的类型)。

​ 在Oracle 和SQL Server等数据库中只有一种存储引擎,所有数据存储管理机制都是一样的。而MySql数据库提供了多种存储引擎。用户可以根据不同的需求为数据表选择不同的存储引擎,用户也可以根据自己的需要编写自己的存储引擎。MySQL 的插件式存储引擎可以让存储引擎层的开发人员设计他们希望的存储层,例如,有的应用需要满足事务的要求,有的应用则不需要对事务有这么强的要求;有的希望数据能持久存储,有的只希望放在内存中,临时并快速地提供对数据的查询。

1.2、存储引擎的类型

1.2.1、InnoDB

InnoDB 支持事务,其设计目标主要面向联机事务处理(OLTP)的应用。其特点是行锁设计、支持外键,并支持类似 Oracle 的非锁定读,即默认读取操作不会产生锁。 从 MySQL 5.5.8 版本开始是默认的存储引擎。

InnoDB 存储引擎将数据放在一个逻辑的表空间中,这个表空间就像黑盒一样由 InnoDB 存储引擎自身来管理。从 MySQL 4.1(包括 4.1)版本开始,可以将每个 InnoDB 存储引擎的 表单独存放到一个独立的 ibd 文件中。此外,InnoDB 存储引擎支持将裸设备(row disk)用 于建立其表空间。

InnoDB 通过使用多版本并发控制(MVCC)来获得高并发性,并且实现了 SQL 标准 的 4 种隔离级别,默认为 REPEATABLE 级别,同时使用一种称为 netx-key locking 的策略来 避免幻读(phantom)现象的产生。除此之外,InnoDB 存储引擎还提供了插入缓冲(insert buffer)、二次写(double write)、自适应哈希索引(adaptive hash index)、预读(read ahead) 等高性能和高可用的功能。

对于表中数据的存储,InnoDB 存储引擎采用了聚集(clustered)的方式,每张表都是按 主键的顺序进行存储的,如果没有显式地在表定义时指定主键,InnoDB 存储引擎会为每一 行生成一个 6 字节的 ROWID,并以此作为主键。

InnoDB 存储引擎是 MySQL 数据库最为常用的一种引擎,Facebook、Google、Yahoo 等 公司的成功应用已经证明了 InnoDB 存储引擎具备高可用性、高性能以及高可扩展性。对其底层实现的掌握和理解也需要时间和技术的积累。如果想深入了解 InnoDB 存储引擎的工作 原理、实现和应用,可以参考《MySQL 技术内幕:InnoDB 存储引擎》一书。

1.2.2、MyISAM 存储引擎

不支持事务、表锁设计、支持全文索引,主要面向一些 OLAP 数 据库应用,在 MySQL 5.5.8 版本之前是默认的存储引擎(除 Windows 版本外)。数据库系统 与文件系统一个很大的不同在于对事务的支持,MyISAM 存储引擎是不支持事务的。究其根 本,这也并不难理解。用户在所有的应用中是否都需要事务呢?在数据仓库中,如果没有 ETL 这些操作,只是简单地通过报表查询还需要事务的支持吗?此外,MyISAM 存储引擎的 另一个与众不同的地方是,它的缓冲池只缓存(cache)索引文件,而不缓存数据文件,这与 大多数的数据库都不相同。

1.2.3、NDB 存储引擎

NDB 存储引擎是一个集群存储引擎,类似于 Oracle 的 RAC 集群,不过与 Oracle RAC 的 share everything 结构不同的是,其结构是 share nothing 的集群架构,因此能提供更高级别的 高可用性。NDB 存储引擎的特点是数据全部放在内存中(从 5.1 版本开始,可以将非索引数 据放在磁盘上),因此主键查找(primary key lookups)的速度极快,并且能够在线添加 NDB 数据存储节点(data node)以便线性地提高数据库性能。由此可见,NDB 存储引擎是高可用、 高性能、高可扩展性的数据库集群系统,其面向的也是 OLTP 的数据库应用类型。

1.2.4、Memory 存储引擎

正如其名,Memory 存储引擎中的数据都存放在内存中,数据库重 启或发生崩溃,表中的数据都将消失。它非常适合于存储 OLTP 数据库应用中临时数据的临时表,也可以作为 OLAP 数据库应用中数据仓库的维度表。Memory 存储引擎默认使用哈希 索引,而不是通常熟悉的 B+ 树索引。

1.2.5、BLACKHOLE

黑洞存储引擎,可以应用于主备复制中的分发主库。往表内插入任何数据,都相当于丢入黑洞,表内永远不存记录。

1.3、如何指定存储引擎

1.3.1、建表时指定

​ create table t1(id int)engine=innodb

​ create table t1(id int)engine=myisam

1.3.2、配置文件中指定

/etc/my.cnf

[mysqld] default-storage-engine=INNODB innodb_file_per_table=1

2、MySQL中的索引

索引:是帮助高效获取数据的数据结构(存储结构),是一个文件。

2.1、MySQL中的索引类型

下图是官网上给出的常用存储引擎的索引类型。

2.1.1、B-TREE索引

MyISAM中索引文件(.MYI)和数据文件(.MYD)是分离的,索引文件只会保存数据的磁盘地址。

主键索引:使用B+Tree作为存储结构,要求KEY是唯一的,叶子节点存放的是数据的磁盘地址。辅助索引:与主键索引在结构上是相同的,但是KEY可以重复,叶子节点同样存放的是数据的磁盘地址。

Ps:主键索引和辅助索引没有多大的区别。这种索引和实际数据分开、索引只是指向了实际的数据,也就是非聚集索引的意思。

InnoDB以主键为索引来组织数据的存储,也就是数据就是主键索引,主键索引就是数据,表中的数据和索引都存放在.IBD文件中。只有主键索引上才会存数据,辅助索引不会存放数据。

主键索引:主键索引的叶子节点包含了完整的数据记录,这种索引叫做聚集索引,因此InnoDB要求表必须有主键,如果没有则会自动选择一个唯一且非空的字段作为主键,如果不存在这种字段则会自动按照隐藏列(-rowid)作为主键,长度6字节,类型为长整型。辅助索引:辅助索引的数据域存放的是主键的值,也就是通过辅助索引搜索需要先检索辅助索引获取主键,然后用主键到主键索引中检索获得数据,这种方式称为回表。如果不需要通过回表也就是一个查询语句的执行只需要从辅助索引中就可以得到查询记录,而不需要查询聚集索引中的记录,就称为索引覆盖,利用覆盖索引直接从索引中获取结果,可以用在如下的场景:Select查询的返回列包含在索引列中;有Where条件时,Where条件中要包含索引列或者复合索引的前导列;查询结果的总字段长度可以接受。

Ps:鉴于上述说明,InnoDB不建议使用过长字段作为主键,因为辅助索引要引用主键索引,主键索引过长会导致辅助索引过长。使用非单调的主键会造成插入数据时由于B+Tree的特性而频繁的分裂合并,因此建议使用自增字段作为主键。

2.1.2、HASH索引

HASH索引是基于HASH表实现的,能以O(1)的复杂度进行查找,但是失去了有序性,因此不能用于外排序①。哈希索引只存储哈希值和行指针,不存储字段值,也就是使用哈希索引必须进行二次查找②,第一次找到对应的行,第二次读取数据。哈希索引不支持范围和模糊查找③,同时可能会出现哈希冲突,因此哈希索引不适用于重复值很多的列上④。哈希索引只支持等值比较查询,如=、IN()、<=>。

InnoDB中有一个特殊功能“自适应哈希索引”,就是当某个索引值被使用的非常频繁时,会在B+Tree索引上在创建一个哈希索引,使得B+Tree具有了快速哈希查找的优点。

2.1.3、FULLTEXT索引

顾名思义,该索引可以用于全文索引,用于查找文本中的关键字,查找条件适用MATCH AGAINST,一般使用倒排索引实现。MySQL5.6.4之后MyISAM和InnoDB都可以支持FULLTEXT索引,但只限于CHAR、VARCHAR和TEXT列。

2.1.4、R-TREE索引

MySQL5.7之前只有MyISAM支持空间类型索引,且索引的字段必须是非空的,用于地理数据存储。5.7之后InnoDB也开始支持,索引以R-TREES的数据结构保存。

Ps: 从其他分类角度来说:

按照物理存储角度分为:聚集索引和非聚集索引。

按照逻辑角度分为:普通索引、主键索引(NOT NULL)、唯一索引(可以为NULL)、联合索引。

联合索引:相当于合并了索引,降低了索引总数。多列索引可以起到几个索引的作用。将查询频率高、选择度好的列按照最左前缀原则组合在一起。在创建一个N列的组合索引时,实际上相当于创建了MySQL可利用的N个索引,比如以a,b,c的顺序创建了联合索引,利用a=?或者a=? and b=?或者a=? and b=? and c=?这三种条件的查询都可以使用这个索引,提高了使用效率。(a和后面的字段中不能存在孔隙)

3、MySQL中的事务和锁原理

MySQL中锁分为表锁和行锁。首先先了解一下事务的概念。

3.1、事务

事务:事务是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。

3.1.1、事务的四大特性ACID
原子性:事务要么都成功要么都失败,Mysql是通过回滚机制(undo log)来保证的。一致性:遵循数据库的完整性约束比如主键唯一,字段长度等以及用户定义的约束。隔离性:主要是在并发场景下,也是为了实现一致性。持久性:也就是数据的保存。将内存中的脏页刷脏到磁盘,MySQL是通过redo log来实现的。
3.1.2、事务并发带来的三大问题
脏读:A查询一条数据,B修改了这条数据但是未提交,A再读发现数据不一致,本质上是因为B修改的数据还存在于内存中。读取到了未提交的数据不可重复读:A查询一条数据,B删除或者修改了这条数据并且提交,A再读发现数据不一致。读取到了修改或者删除的已提交的数据幻读:A查询一条数据,B新增加了数据并且提交,A再读发现数据变多。读取到了插入并已提交的数据

Ps:事务并发的三大问题其实都是数据库读一致性的问题,数据库必须提供一定的事务隔离机制来解决。

3.1.3、事务的四种隔离级别
读未提交 Read Uncommitted读已提交 Read Committed:解决脏读可重复读 Repeatable Read:解决脏读(InnoDB)、不可重复读串行化 Serializable:解决事务并发的所有问题

Ps:InnoDB是通过临键锁的阻塞插入解决幻读

3.1.4、事务隔离级别的解决方案
在读取数据前,对其加锁,阻止其他事务对数据进行修改 Lock Based Concurrency Control (LBCC)生成一个数据请求时间点一致性数据快照(Snapshot),并用这个快照来提供一定级别(语句级或事务级)的一致性读取 Multi Version Concurrency Control (MVCC)

3.2、锁原理

3.2.1、表锁与行锁的区别
锁定粒度 表锁 > 行锁加锁效率 表锁 > 行锁冲突概率 表锁 > 行锁并发性能 表锁 < 行锁

PS:MyISAM只能支持表锁,InnoDB表锁和行锁都支持

3.2.2、锁的模式Lock Mode
共享锁(行锁)Shared Locks 又称读锁S锁。就是多个事务对同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。 加锁方式:select * from student where id=1 LOCK IN SHARE MODE。释放锁:commit/rollback 排它锁(行锁)Exclusive Locks 又称写锁X锁,排它锁不能与其它锁并存,如一个事务获取了一个数据行的排它锁,其他事务就不能再获取该行的锁(共享锁、排它锁),只有该获取了排它锁的事务是可以对数据进行读取和修改。 加锁方式:自动:delete/update/insert默认加上X锁。手动:select * from student where id=1 FOR UPDATE; 意向共享锁(表锁)Intetion Shared Locks 简称IS锁,表示事务准备给数据行加入共享锁,也就是说一个数据行加共享锁前必须先取得该表的IS锁。意向排它锁(表锁)Intetion Exclusive Locks 简称IX锁,表示事务准备给数据行加入排它锁,说明事务在一个数据行加排它锁前必须先取得该表的IX锁。

Ps:意向锁是由数据引擎自己维护的,用户无法手动操作意向锁。给一张表成功加上一个锁的前提就是没有任何事务已经锁定了这张表的任意一行数据,意向锁也就相当于一个标记,比如如果该表有意向锁,表示该表的某一行或多行已经有锁,那么给该表加锁就会失败,程序就会堵塞。这样就提高了加表锁的效率,不必再扫描每一行是否都没有锁。

Ps:锁本质上锁住的是索引。

3.3、行锁算法

3.3.1、记录锁Record Locks

对索引项加锁,精准匹配,记录锁锁住的是一行记录。

3.3.2、间隙锁Gap Locks

对索引项之间的间隙进行加锁,第一条记录之前的间隙,或最后一条记录后的间隙加锁。只有Read Repeatable中存在, 阻塞插入,也就是解决了幻读的问题。所以说InnoDB中的RR隔离级别已经通过间隙锁解决了幻读的问题

3.3.3、临键锁Next-Key Locks

临键锁相当于Record Locks + Gap Locks 是InnoDB默认的锁算法,左开右闭,最后的范围是最后一个record的下一个左开右闭的区间。 下面看个示例:

最新回复(0)