- 专栏内容:MySQL
- ⛪个人主页:子夜的星的主页
- 座右铭:前路未远,步履不停
索引可以简单理解为是一本书的目录。
索引是一种特殊的文件,包含着对数据表里所有记录的引用指针。可以对表中的一列或多列创建索引,并指定索引的类型,各类索引有各自的数据结构实现。
一个表中有很多数据,在查询数据的时候,最基本的方法就是遍历表,一条条的进行筛选。因此,就可以给这个表建立索引,来提高查找速度。索引是以“列”为维度进行建立的。
索引是用来提高查询效率的,但是也有一定的缺陷:
create table student (id int primary key , name varchar(20));
create table student (id int unique , name varchar(20));
create table student (id int primary key , name varchar(20),classId int ,foreign key (classId) references class (classId));
当表中存在主键的时候,内部就会自动给这个列创建索引。
因为主键不允许重复,因此进行插入或者修改,就需要先进行查询,看看插入/修改的结果是不是已经存在。所以,建立索引后就会加快频繁的查询速度。
前面的都是自动创建的索引,下面我们了解一下手动创建索引的方法。
create index 索引名 on 表名(字段名)
create index student_name on students(name);
手动创建索引操作,可能会非常危险。如果表是空的或者表里面包含的数据本身就不多,这个时候创建索引就没有事。但是,如果表非空,并且里面包含了非常多的数据,那么创建索引会引起非常大规模的硬盘 IO 操作,进一步导致数据库被卡死。
show index from 表名;
drop index 索引名 on 表名;
删除索引,只能针对手动创建的索引。自动生成的索引,是不能被删除的。
删除索引的操作也是非常危险的,因为也会出现大规模的硬盘 IO
【问题】如果现在确实需要给一个已经有很多数据的表创建/删除索引,并且这个数据库还是生产环境的数据库,怎么办?
数据库服务器往往不是单台服务器,为了系统的可靠性,往往会搞多个 MySQL 服务器节点,这些节点的数据都是一样的,能够提供相同的服务。所以,我们可以先准备一台新的服务器,把表和索引都创建好,然后把数据都导入过来,再把要替换的 MySQL 服务器关闭,把新的 MySQL 服务器替换上去就行了。
MySQL 的索引的数据结构到底是什么样的数据结构,并不是定式。这取决于 MySQL 使用那个存储引擎(MySQL 这个程序包含很多模块,用来存储数据的模块就是存储引擎)。具体如何存储数据,MySQL 支持多种存储方案,当下最主流的方式是 innodb
。
以往的数据结构都是在内存中的,数据库这块组织数据使用的数据结构则是在硬盘上的。内存上的数据结构,对访问操作来说不敏感。找数据的过程花费的时间多,真正访问的时间不多。硬盘上的数据结构,对于访问操作来说,比较敏感。而读写一次硬盘开销又远大于内存。
索引主要目的是为了进行快速查找,hash 不能够进行范围查询,不能够进行模糊匹配。而红黑树,能进行范围查询和模糊匹配,但是会引入较多的硬盘 IO 操作。所以,innodb
这个存储引擎的底层数据结构是B+树。B +树是 B 树的一种改进。B 树的核心思路和之前介绍过的二叉搜索树差不多,B 树本质上是一个 N 叉搜索树。
MySQL索引的底层数据结构主要是B+树和哈希表。
=
, IN()
等操作。哈希索引的优势在于快速的查找速度,但它不支持范围查找。在MySQL中,哈希索引主要被Memory存储引擎使用。除此之外,MySQL还支持全文索引和空间索引等,但这些索引的底层数据结构和B+树及哈希表有所不同。全文索引用于文本数据的搜索,而空间索引用于地理空间数据。
使用 B+树作为主要索引的关键优势:
事务指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败。 在不同的环境中,都可以有事务。对应在数据库中,就是数据库事务。事务的本质就是为了把多个操作打包成一个操作来完成。(打包成一个操作:要么全部都执行成功,要么一个都不执行)
【注意】一个都不执行,并不是真的不执行。执行不执行成功,执行了才知道。
假设事务中有 3 个操作:先执行 1 再执行 2 最后执行 3,如果执行到中间出错了,就需要自动把前面已经成功执行的操作还原回最初没有执行的模样。本质上,这里的一个都不执行,并不是没有执行,而是看起来跟没执行一样。这个还原数据的过程,叫做回滚(rollback)。
【问题】回滚是咋实现的 ?
只需要把事务中执行的每个操作,都记录下来(通过特定的日志)。如果需要回滚,就按照之前的操作进行逆操作即可。
(1)开启事务:start transaction;
(2)执行多条SQL语句
(3)回滚或提交:rollback/commit;
说明:rollback
即是全部失败,commit
即是全部成功。一个事务,必须以 commit
或 rollback
这两个操作结尾。如果没有这两个操作,接下来的各种 sql 操作都会被认为是事务的一部分。
原子性: 事务是一个不可分割的工作单元,事务中的操作要么全部完成,要么全部不完成。如果事务中的一个操作失败,整个事务将回滚到事务开始之前的状态,就像这些操作从未被执行过一样。
一致性: 事务必须保持数据库的一致性。这意味着事务的执行结果必须使数据库从一个一致的状态转换到另一个一致的状态。一致性包括数据的完整性、业务规则、关系约束等。
隔离性: 并发执行事务的适合,隔离性会在执行效率和数据可靠之间做出权衡。“隔离”描述的是同时执行的事务之间,相互影响。隔离性越高,并发就越低,数据越可靠,性能就越低。 隔离性通过不同的隔离级别实现,如可串行化、读已提交、读未提交和可重复读,每个级别都有不同的并发控制方法和可能遇到的问题(如脏读、不可重复读、幻读)。
【问题】什么是脏读、不可重复读、幻读?
- 脏读(Dirty Read):
- 定义:脏读发生在一个事务读取了另一个未提交事务的数据。如果那个未提交的事务最终失败并回滚,那么第一个事务读取的数据就是不一致的,因为它读取了永远不会被提交的数据。
- 例子:事务 A 修改了一条记录,但还没有提交。与此同时,事务 B 读取了同一条记录。如果 A 回滚了它的改变,B 读取的数据就是无效的。
- 解决:写加锁,提交之前不可读。
- 不可重复读(Non-repeatable Read):
- 定义:不可重复读指的是在同一个事务中,两次读取同一数据集合时,由于其他事务的修改操作,导致两次读取的数据不一致。
- 例子:事务 A 读取了一条记录,随后事务 B 修改了这条记录并提交。当事务 A 再次读取同一记录时,发现数据已经发生变化。
- 解决:读加锁,读的适合不可写。
- 幻读(Phantom Read):
- 定义:幻读与不可重复读类似,但它涉及到数据集合的数量变化。一个事务在读取某个范围的记录时,另一个事务插入或删除了该范围内的记录,导致第一个事务再次读取时发现有“幻影”数据。
- 例子:事务 A 读取了一个范围内的所有记录。同时,事务 B 插入了一个新的记录到这个范围内。当事务 A 再次读取这个范围的记录时,会发现一个之前未见的新记录。
- 解决:彻底串行化,完全放弃并发执行。
持久性: 一旦事务提交,对数据所做的更改就是永久性的,即使系统发生故障也不会丢失。持久性通常通过数据库管理系统的恢复和日志机制来保证。
读已提交(Read Committed):
可重复读(默认)(Repeatable Read):
串行化(Serializable):