目录
1.2.1 查看索引:show index from 表名;
1.2.2 创建索引:create index 索引名 on 表名(列名);
1.2.3 删除索引:drop index 索引名 on 表名;
1.索引
1.1 前言
当数据库使用select查询时,会先遍历表,把当前的行带入条件中,看条件是否成立,成立就保留,不成立就跳过。这样的查询遍历成本非常大,时间复杂度起码是O(n)。数据库的数据是存储在硬盘上的,每一条记录都要访问硬盘,开销会特别大。
因此引入了索引这样的方便查询的概念,它会避免表的遍历,加快查询速度。
索引能够提高查询速度,但也有代价:
- 占用更多空间。生成索引需要用到一系列数据结构和一系列数据,来存储到硬盘空间中
- 会降低插入修改删除的速度。每次数据的更新都要将索引一同更新,存在额外时间开销。
在开发过程中查询的频率比插入修改删除的频率高得多。
1.2 索引的相关操作
1.2.1 查看索引:show index from 表名;
当表中的列存在unique、主键和外键的时候,系统会自动生成索引,因为unique和主键都是唯一值,需要频繁地进行查询操作,外键约束的子表需要不断查询父表对应的字段,父表的记录删除时也需要查询子表中有没有引用该记录。因此这三个约束会自动生成索引。
每个表可以有多个索引,就像字典中有多种目录(部首目录、拼音目录、偏旁目录……)有多个索引代表有多种查询方式,但是消耗的资源也相对多一点。
1.2.2 创建索引:create index 索引名 on 表名(列名);
创建索引是非常危险的操作。创建索引需要针对现有的所有数据进行大规模整理,如果是一个空表或存放的数据不多,创建索引还是可以的,但是如果表放的数据很多,很容易会把服务器很卡住,(要是非得在这种情况下创建索引,可以在搞一个机器,部署同样的mysql服务器,创建同样的表,把表上的索引设置好,再把原来的数据库中的数据导入到这个数据库中,导入过程控制速度,这样就能保证不影响原来服务器的运行了。)
1.2.3 删除索引:drop index 索引名 on 表名;
手动创建的索引能够删除,自动生成的索引删除不了。
删除索引也是危险操作,要是实在要删除,应该要使用上述创建索引的策略进行操作。
1.3 索引的实现原理
1.3.1 引入B+树
索引是通过一系列的数据结构来实现的。那么是使用哪种数据结构进行实现的呢?
顺序表、链表、栈、队列、堆都不适合查询,适合查询的有二叉搜索树(红黑树)、哈希表,但是这两种数据结构都不适合数据库作为索引,对哈希表来说,只能进行精准查询,而无法做到范围查询、模糊匹配;对二叉树来说,既能够精准查询也能够范围查询和模糊匹配(“孙%”可以,“%孙”不行),不适合索引的数据结构的原因不在这,在于红黑树是二叉树,度只有2,数据多了树的高度就会很高,一旦高度高了,查询的时候IO访问的次数就更多(查询到每个节点都会访问一次IO)。
因此为了让树的高度不那么高,可以增加树的度来降低树的高度。这种数据结构叫做B+树(N叉搜索树)。
1.3.2 简单了解B树
认识B+树之前,先了解一下什么是B树。B树是一个n叉搜索树,每个节点的度是不确定的,一个节点可以有多个key,n个key可以划分出最多n+1个区间,每个区间都能生成一颗子树(节点)。
一个节点里的key的个数不是无限制的,当一个节点中的key达到一定数量时,会触发节点的分裂,当删除key达到一定数目,也会进行节点的合并,如:
1.3.3 认识B+树
B+树时B树的改进版,针对数据库量身定做的。
1.3.3.1 B+树的特点
- B+树也是N叉搜索树,一个节点有N个key,可以划分N个区间(不是N+1),每个节点最后一个key后面不能划分区间。
- 每个节点的最后一个key是当前子树的最大值。
- 父节点的key都会以最大值的身份出现在对应区间的子节点中,这样的意义是,叶子节点这一层会包含整个树的key。
- B+树会用链表这样的结构将叶子节点串起来。此时就可以很方便地进行数据的遍历,并且很方便地在数据集合中取出一个范围的子集。
1.3.3.2 B+树的优点
- N叉搜索树,高度有限,降低访问IO的次数。
- 非常擅长范围查询
- 所有查询都会在叶子节点上进行,每次查询的时间开销都是基本一致的,更加稳定。
- 非叶子节点仅存储key,叶子节点存储整行记录的数据(这样就可以提前把非叶子节点的数据缓存到内存中,查询到时候直接在内存中进行,进一步地减少IO的访问)
2.事务
2.1 事务的概念
实际开发中,有些场景需要“一气呵成”地完成操作,中间不能报错,不能出现问题。比如转账:
如果出现这样的问题,就是严重错误了,为了解决这种数据不上不下的问题,引入了事务这样的概念。
事务是指逻辑上的一组操作。将多个sql语句打包成一个整体,这样的特点称为“原子性”。要么全部执行完,要么“一个都不执行”。此处的都不执行并不是真的没有执行,而是执行到一半出错了,后面的语句没有被执行到,MySQL就会发现此处出现问题了,对执行过的语句进行回退,回退成没执行前的样子,这样的操作称为回滚(rollback)。回滚的原理是以日志的方式,记录事务当中的重要操作。这样的记录就是回滚的依据。
2.2 事务的使用
开启事务:start transaction
提交:commit
回滚:rollback
2.3 事务的特性
2.3.1 原子性
将多个sql语句打包成一个整体,保证一系列操作能够“执行正确”或“恢复如初”。
2.3.2 一致性
在事务执行前和执行后,数据都不能太离谱,很多时候要靠数据库约束和一系列检查机制来完成的。
2.3.3 持久性
事务做出的修改,都是在硬盘上持久保存的。重启服务器,数据仍然存在,事务的修改仍然是有效的。
2.3.4 隔离性
这是在数据库并发执行多个事务会涉及到的问题。MySQL是一个客户端服务器结构的程序,一个服务器可以给多个客户端提供服务。多个客户端都可以让数据库执行事务,并发是非常常见的。
如果希望为了提高效率而提高并发程度,就会导致数据可能会出现一些“错误”。
隔离级别就是在“执行效率”和“数据准确性”之间做权衡。往往提高效率就会降低正确性,提高正确性就会降低效率。
在并发执行事务时,会出现哪些问题呢?
2.3.4.1 脏读
一个事务A在写数据的过程中,事务B在读数据,此时如果事务B读完了,事务A还在写或者改数据,那事务B读到的数据就是不完整的或是错误的。这种现象就叫做脏读。
解决的方案是给写操作加锁,在写数据的时候,不允许进行读操作。写操作完成后才可进行读操作,这样的执行方式虽然会降低效率,但可靠性高了,数据准确性提高了。
2.3.4.2 不可重复读
一个事务A在读数据的过程中,事务B在写操作,此时如果事务A中要读取多次数据,在这过程中事务B改写了数据,事务A接下来读到的数据将会与之前读到的数据不一致,这种情况就是不可重复读。如果是两个事务间读到的数据不一致,是没有问题的,如果是同一个事务中,读到的数据不一致,就是不能接受的。
解决方案是给读操作加锁,在读数据的过程中,不允许进行写操作。
2.3.4.3 幻读
事务A在文件1中读数据的过程中,事务B在文件2中写数据,此时如果在事务A读数据的时候,事务B写完了,等到事务A读完数据之后,就会发现多出了个文件2。虽然是事务A读的数据内容没变,但是整体的数据集变了,这种情况称为幻读。幻读是否需要解决要结合具体场景分析,可以是问题也可以不是问题。
解决方法是多个事务之间使用串行的方式进行执行,完全不使用并发执行,这样的执行方式就会使执行效率降低很多,但可靠性更高。
2.3.4.4 隔离级别
要在效率和准确性之间做权衡,不同场景中的要求不同,MySQL中提供了“隔离级别”,可以针对不同隔离程度进行设置,应付不同场景。有的场景追求准确度,效率是其次,比如充值、转账等一系列场景;有点场景追求效率,其次是准确度,比如短视频点赞、评论、转发。
MySQL提供的“隔离级别”总共有四种,可以针对不同需求进行设置,可以在MySQL中的配置文件中进行设置隔离级别。
四种级别,对应上述三个问题
MySQL默认的隔离级别是repeatable read(可重复读),这种隔离级别可以应付开发中绝大多数场景。