目录
mysql索引事务
我们之前使用select查询的时候,总是需要进行如下步骤:
1、先遍历表;
2、把当前的行给带入到条件中,看条件是否成立;
3、条件成立,这样的行就保留,不成立就跳过。
如果表非常大,这样的遍历成本就非常高了,至少是o(n)。
数据库是把数据存储在硬盘上,每次读取一个数据,都需要读取硬盘,这个开销本身就很大。
所以,我们引入“索引”,它是属于针对查询操作引入的优化手段。我们可以通过索引来加快查询的速度,避免针对表进行遍历。
索引是能够提高提高查询速度的,但是也有代价:
-
占用磁盘空间:索引本身需要存储在磁盘上,因此会占用额外的存储空间。生成索引是需要一系列的数据结构以及一系列的额外的数据来存储到硬盘空间中的。对于大型数据表,索引可能占据相当大的磁盘空间。
-
降低插入、修改和删除效率:当执行插入、修改和删除操作时,数据库不仅需要更新数据表中的数据,还需要更新相关的索引。这会导致这些操作变得更为耗时,特别是在有多个索引的情况下。
索引(index)
概念:
-
特殊文件:索引是一种特殊的数据结构,它不是实际的数据表,而是包含对数据表中记录的引用指针的数据结构。这些引用指针可以是记录的物理存储位置或者是主键值等。
-
列或多列的引用:可以为数据表中的一列或多列创建索引。这意味着我们可以根据需要创建单列索引或复合索引,复合索引涵盖了多个列。
-
索引类型:mysql支持多种不同类型的索引,包括b树索引、哈希索引、全文索引等。每种索引类型都有其适用的场景和特点。
作用:
-
提高查询性能:索引的主要作用是提高数据库查询的速度。通过使用索引,数据库可以快速定位和检索数据,而不必扫描整个表格。
-
支持快速数据定位:索引类似于书籍目录,它提供了一个快速查找数据的方式。无论是单列索引还是复合索引,都能够迅速定位到匹配查询条件的记录。
-
加速连接操作:当进行连接操作(例如join语句)时,索引可以显著提高查询的速度,尤其是在连接大型表格时。
-
确保数据完整性:索引可以用于确保数据的完整性。例如,唯一索引可以防止重复的数据插入,主键索引可以确保每行记录都有唯一标识。
-
支持排序和分组:索引还可以用于加速排序和分组操作,从而提高相关查询的性能。
总的来说,mysql中的索引是数据库性能优化的关键工具之一。但是,要谨慎使用索引,因为不正确或过多的索引可能会导致性能下降,特别是在插入、更新和删除操作频繁的情况下。因此,在设计数据库时,需要根据具体的查询需求和数据模型选择合适的索引,并定期进行索引的维护和优化。此外,索引与事务无直接关联,但它们可以一起使用,以提高数据库的性能和数据完整性。
使用场景:
-
数据量较大,且频繁条件查询:当数据表包含大量数据,并且我们经常需要在某些列上执行条件查询时,索引可以显著提高查询性能。索引允许数据库引擎快速定位和检索匹配条件的记录,而不必扫描整个表格。
-
插入和修改操作频率较低:创建索引会增加插入、更新和删除操作的开销,因为索引也需要相应地更新。如果插入和修改操作频率较低,那么这个开销相对较小,索引的性能提升会更有价值。
-
磁盘空间充足:索引本身需要存储在磁盘上,因此需要额外的磁盘空间。因此需要确保我们的存储空间足够大以容纳索引数据。
索引的相关操作
在数据库中,有两种主要类型的索引:自动生成的索引和手动创建的索引。
1. 自动生成的索引:
- 当我们在数据库表中定义主键约束(primary key)、唯一约束(unique)或外键约束(foreign key)时,数据库管理系统通常会自动创建相应的索引,因为每次使用这些约束都会进行非常频繁的查询操作。
- 主键约束会自动创建一个主键索引,用于确保表中的每行都具有唯一的主键值。
- 唯一约束会自动创建唯一索引,确保被约束列或列组合中的值是唯一的。
- 外键约束通常也会创建索引,以提高在关联表之间执行连接操作的性能。
这些自动生成的索引是数据库管理系统的一部分,它们帮助确保数据的完整性并提高了一些查询的性能。

这个时候这张表里是没有任何索引的。
我们把这张表删了,重新创建一下:


unique也是一样的:

让我们再来看看外键的:



2. 手动创建的索引:
- 除了自动生成的索引之外,数据库管理员和开发人员还可以手动创建索引,以满足特定查询需求和性能优化目标。
- 手动创建索引可以针对经常执行的查询,特别是在大型数据表上执行查询的情况下,显著提高性能。
- 手动创建索引时,我们可以选择创建单列索引或复合索引,以满足特定的查询条件。
自动生成的索引用于维护数据完整性和提高某些查询的性能,而手动创建的索引则用于更精确地满足特定查询需求,并进一步优化数据库的性能。
查看索引
show index from 表名;
创建索引
对于非主键、非唯一约束、非外键的字段,可以创建普通索引。
create index 索引名 on 表名(字段名);
我们先来看看表的结构,决定把student表的name这一列也加上索引:


但是创建索引也是一个危险操作。创建索引的时候需要针对现有的数据进行大规模的整理。
表的大小和数据量对于创建索引的影响非常重要。对于小型表格或空表,创建索引通常不会导致太大问题。但是,对于包含大量数据的表格,创建索引可能会导致数据库服务器过载,尤其是在高负载的环境中,那么创建索引也就很容易把数据库服务器卡住。
一般创建索引都是在创建表的时候就提前规划好了的。一旦表已经使用很久了,有很多数据,再想要修改索引,就需要慎重了。
非要创建索引该怎么办呢?我们就需要使用一些其他的技巧:
比如在搞另外一个机器,部署mysql服务器,也创建同样的表,并且把表上的索引创建好。再把之前那台机器上的数据控制节奏的一定的导入到新的mysql服务器上,多花点时间导数据都没事,不要影响到原来服务器的正常运转。当所有数据都导入完毕,就可以使用新的数据库来替换旧的数据库了。
我们刚刚提到的方法就是一种常见的数据库迁移和索引创建策略,通常被称为"表的预填充"(table pre-fill)或"热备份切换"(hot backup switch)策略。
这个策略允许在不中断正常数据库操作的情况下创建索引或进行其他维护操作。以下是一些关于这种策略的注意事项:
-
创建备份服务器:需要设置一个新的mysql服务器作为备份服务器,确保其硬件和配置与原始服务器相匹配。
-
创建表结构和索引:在备份服务器上创建与原始服务器上的表结构和索引完全相同的数据库和表。
-
导入数据:从原始服务器中以受控的方式导入数据到备份服务器。这可以通过多种方式来实现,例如使用数据库备份工具、数据导出和导入工具,或自定义数据迁移脚本。
-
创建索引:在备份服务器上创建所需的索引。由于这个操作是在备份服务器上进行的,不会对原始服务器的性能产生影响。
-
切换到备份服务器:一旦备份服务器上的数据和索引都准备好,并且与原始服务器的数据保持同步,我们就可以将应用程序重定向到备份服务器上。
-
监控同步:在切换到备份服务器后,需要继续监视数据同步,确保备份服务器上的数据保持与原始服务器同步。
-
回滚计划:如果在备份服务器上出现问题,可以随时回滚到原始服务器。因此,这种方法在风险管理方面具有灵活性。
删除索引
手动创建的索引可以手动删除,如果是自动创建的索引(主键/外键/unique),是不能删除的。
drop index 索引名 on 表名;


删除索引也是一个危险操作!!!
数据库和数据实在是太重要了!
索引背后的原理
索引也是通过一定的数据结构来实现的。
我们把每个数据库过一遍,通过排除操作确定索引背后的数据结构:
1、顺序表(数组):
顺序表是一种线性数据结构,可以通过下标(索引)直接访问元素,因此查找速度非常快,时间复杂度为 o(1)。
但对于插入和删除操作,特别是在中间位置插入或删除,性能较差,需要移动大量元素,时间复杂度为 o(n)。
在数据库索引中,顺序表不常用于主索引,因为主索引通常需要支持高效的插入和删除操作。
2、链表:
链表是一种动态数据结构,擅长中间位置的插入和删除,时间复杂度为 o(1)。
查找元素需要遍历链表,平均时间复杂度为 o(n)。
在数据库索引中,链表通常不作为主索引的实现,但可以用于辅助结构,如某些类型的倒排索引。
3、栈和队列:
栈和队列是基于线性数据结构的,它们主要用于特定的操作序列,如先进先出(fifo)或后进先出(lifo)。
它们不是用来实现通用数据库索引的数据结构,而更多地用于应用程序的运行时栈和队列操作。
4、堆:
堆是一种树状数据结构,用于实现优先队列,不常用于通用数据库索引。
在某些数据库系统中,堆可以用于实现某些特殊类型的索引,如全文搜索索引。
5、二叉搜索树(bst):
二叉搜索树是一种有序树状数据结构,每个节点都有左子树和右子树,具有良好的查找性能。
一个普通的二叉搜索树时间复杂度是 o(n) ,因为会存在不平衡的极端情况——单枝树。在最坏情况下,未平衡的bst可能导致查询性能下降,时间复杂度为 o(n)。
为了保持平衡,可以使用平衡二叉搜索树(如avl树、红黑树)来实现索引,以确保查询性能在平均情况下为 o(log n)。
-
avl树:
- avl树是一种自平衡的二叉搜索树,它确保了树的左子树和右子树的高度差不超过1,因此在最坏情况下保证了查询性能为 o(log n)。
- 由于要求平衡非常严格,avl树可能需要频繁进行旋转操作来维持平衡,这可能会导致较高的维护开销。
- 适用于对查询性能要求非常高,而对插入和删除性能要求较低的情况。
-
红黑树:
- 红黑树也是一种自平衡的二叉搜索树,但相对于avl树,它对平衡的要求稍微宽松一些,允许左右子树高度差在一定范围内。
- 红黑树的维护开销通常比avl树低,因为不需要频繁的旋转操作来保持严格的平衡。
- 适用于需要平衡查询性能和插入/删除性能的情况,特别是在动态数据集中。
在数据库索引中,通常选择avl树或红黑树之一来实现索引结构,具体选择取决于应用程序的需求。如果查询性能是首要关注点,而且可以容忍较高的维护开销,那么avl树可能是一个更好的选择。如果需要平衡查询性能和插入/删除性能,并且可以接受稍微宽松的平衡要求,那么红黑树可能更适合。
在实际应用中,红黑树通常是一种更常见的选择,因为它在大多数情况下能够提供良好的平衡。
6、哈希表
哈希表(hash table)是在数据库索引中可以实现非常快速查找的数据结构之一,可能是我们最好的选择了。
其查找操作的平均时间复杂度为 o(1)。
哈希表通过将键(索引列的值)映射到一个固定大小的数组(哈希表桶)来实现快速查找。然后,通过在哈希表中查找特定的键,可以直接访问与之关联的数据,而不需要进行线性搜索。
优点:
- 快速查找:哈希表提供了常量时间复杂度的查找操作,无论数据量有多大,查找时间几乎始终相同。
- 高效的插入和删除:插入和删除操作通常也具有良好的性能,平均时间复杂度为 o(1)。
但需要注意的是,哈希表也有一些潜在的问题和注意事项:
- 碰撞(collision):当不同的键被映射到相同的哈希桶时,发生了碰撞。处理碰撞需要使用冲突解决策略,例如链地址法(chaining)或开放地址法(open addressing)。
- 哈希函数选择:选择适当的哈希函数对于哈希表的性能非常重要。一个糟糕的哈希函数可能会导致碰撞的频繁发生,降低了性能。
- 空间需求:为了实现 o(1) 的平均查找时间,哈希表需要足够的空间,通常要大于数据的实际大小,这可能会占用较多的内存。
- 不适用于范围查询:哈希表适用于精确匹配的查找,但不适用于范围查询(例如,查找在某个范围内的所有记录)。
总的来说,哈希表在数据库索引中通常用于支持快速查找操作,尤其是在主内存数据库或缓存中。但需要谨慎选择哈希函数和考虑数据的特性,以确保良好的性能和避免碰撞。同时,对于一些特殊查询需求,可能需要结合其他索引类型来实现更全面的索引支持。
综上,适合我们索引的就只有“二叉搜索树”和“哈希表”。
但是这两个数据结构都不适合数据库作为索引。
对于哈希表,只能进行“精准匹配”,无法进行范围查询( where id > 100 and id < 250 ),更不能进行“模糊匹配”( where name like '孙%' )。而这些查询操作在数据库中是非常常见的。
红黑树是可以精准匹配,也能范围查询,也能模糊匹配(只不过不够全面)。但是红黑树也是二叉树。每个结点最多两个子树。由于 biaoshi树的分叉少(度少),此时表示同样数量的结果集合,树的高度就会更高。一旦树的高度更高了,查询的时候io的访问次数也就会更多。
因此,数据结构引入的索引是一个改进的树形结构,叫“b+树”(n叉搜索树)。
为了了解b树,我们还需要先了解一下b树。
b树
b树(balanced tree,也称为b-树)(注意这里的“-”不是减号,而是连接符)是一种自平衡的多路搜索树数据结构,广泛用于数据库和文件系统等需要高效存储和检索大量数据的应用中。b树的设计目标是在磁盘存储等外部存储设备上高效地管理大量数据,并支持快速的查找、插入和删除操作。
-
节点的度和区间:每个b树节点可以包含多个键,这决定了节点的度。每个节点的度都是不确定的,一个节点上保存了n个key,就划分出了n+1个区间,每个区间都可以衍生出一系列的子树…… 这些区间对应于子树中的数据范围。这种方式使得在查找时可以快速确定要访问的子树,从而减少磁盘i/o次数,树的高度也就大幅度降低了。
-
磁盘i/o和节点读取:b树的设计目标之一是最小化磁盘i/o操作。由于每个节点通常与磁盘块大小相匹配,一次磁盘读取可以获取整个节点的内容,这减少了磁盘i/o的次数。相对于磁盘读取,比较操作通常更轻量级,因此b树的结构有助于减少访问外部存储设备的次数,提高了性能。
-
节点的分裂和合并:b树的自平衡性体现在节点的分裂和合并操作上。一个节点中,虽然可以保存n个key,但是也不是无限制的。当一个节点达到一定规模时,它会分裂成两个节点,并将其中一半的键移到新的节点中。相反,当节点中的键数量减少时,可能会触发节点的合并操作,以减少树的高度。这些操作有助于保持树的平衡和高效性。
使用b树进行查找操作时,通常会遵循以下步骤:
-
从根节点开始:查找操作通常从b树的根节点开始。如果树是空的,查找操作结束并返回未找到的结果。
-
在节点中查找键:从根节点开始,比较要查找的键(目标键)与节点中存储的键。通常使用二分查找或其他高效的查找算法来确定目标键在节点中的位置。
-
分支到子节点:根据目标键与节点中键的比较结果,确定应该分支到哪个子节点。b树的每个节点都对应一个区间范围,根据比较结果,选择对应区间的子节点。
-
重复查找:重复上述步骤,直到达到叶子节点。叶子节点是b树中不包含子节点的节点,通常存储了最终的数据或者指向实际数据的指针。
-
在叶子节点中查找键:在叶子节点中,继续查找目标键。这可以是二分查找或线性查找,具体取决于叶子节点中键的排列方式。如果找到目标键,查找操作成功并返回相关数据或指针;如果未找到,查找操作结束并返回未找到的结果。
-
返回结果:查找操作根据结果返回所需的数据或指针。如果找到目标键,将返回与之关联的数据;如果未找到,将返回未找到的标志。
b树的主要特点和介绍:
-
多路搜索树:b树是一种多路搜索树,每个节点可以包含多个子节点。与二叉搜索树不同,b树的节点可以有更多的子节点,通常称为分支因子(或度)。
-
平衡性:b树具有自平衡的特性,保持了整棵树的平衡。这意味着在任何时候,从根节点到叶子节点的路径长度几乎是相等的,这有助于保持高效的查询性能。
-
有序性:b树的节点存储数据并按照升序(或降序)排列,使得在b树上进行范围查询非常高效。
-
适用于外部存储:b树的设计考虑了在外部存储设备(如磁盘)上存储数据的需求。每个节点的大小通常与磁盘块大小相匹配,以最大限度地减少磁盘i/o操作。
-
支持插入和删除:b树支持高效的插入和删除操作,这些操作可以在 o(log n) 的时间内完成,其中 n 是树中的节点数。
-
常见应用:b树广泛应用于数据库系统中,用于实现索引结构,以支持快速的查询操作。它还用于文件系统中,帮助管理文件的物理布局。
-
变种:b树有多个变种,包括b+树和b*树。b+树是一种最常见的变种,它在b树的基础上做了一些优化,如将数据仅存储在叶子节点,提供了更好的范围查询性能。
接下来我们就来看看b+树。
b+树
b+树是b树的改进,针对数据库量身定做的。
b+树也是一个n叉搜索树,一个节点上存在n个key,划分为n个区间。
每个节点上n个key中,最后一个就相当于当前子树的最大值(也可以设置为第一个为当前子树的最小值)。
父节点上的每个key都会以最大值的身份在子节点的对应区间中存在(key可能会重复出现)。
这样做有一个很大的意义:
这就使叶子节点这一层包含了整棵树的数据全集。
b+树会使用链表这样的结构,把叶子节点串起来。 此时我们就可以非常方便的完成数据集合的遍历,也可以非常方便的从数据集合中按照范围取出一个“子集”。

总结下,b+树的主要特点和介绍:
1. 多路搜索树: b+树是一种多路搜索树,每个节点可以包含多个键(key),通常称为阶数(或度数),记为n。与b树一样,b+树的节点可以有多个子节点。
2. 有序性: b+树的节点按照键的升序排列。这一特性使得b+树非常适合范围查询,因为在树中进行范围查询时,只需从根节点开始沿着树的路径移动到所需的区间即可,无需进行额外的排序操作。
3. 叶子节点链接: 与b树不同,b+树的叶子节点之间通常通过链表相连。这意味着叶子节点是有序的,并且可以轻松地遍历整个数据集。这对于范围查询非常有用,因为可以在叶子节点之间沿着链表移动,获取范围内的数据。
4. 叶子节点存储数据: b+树的叶子节点包含了所有数据的引用(通常是指向实际数据的指针)。这意味着在叶子节点上进行查找操作,可以直接找到所需的数据。
5. 内部节点仅用于导航: 与b树不同,b+树的内部节点不包含实际数据。它们仅用于导航,即确定应该分支到哪个子节点。
6. 高度平衡: 由于b+树的叶子节点之间通过链表相连,并且每个节点的度数通常较高,整棵树的高度通常非常平衡。这有助于保持查询性能的稳定性,无论数据量多少。
7. 插入和删除操作: b+树支持高效的插入和删除操作,通常具有稳定的性能。当节点的键数量达到阶数n时,会触发节点的分裂操作,使树保持平衡。删除操作也可以触发节点的合并,以减小树的高度。
b+树在某些方面相对于b树、哈希表和红黑树具有一些优点,特别是在数据库系统和文件系统等需要高效存储和检索大量数据的应用中。
b+树相对于其他数据结构的优点:
1. 适用于外部存储:b+树的设计目标之一是在外部存储设备(如硬盘)上高效存储大量数据。与内存中的数据结构不同,b+树的节点大小通常与磁盘块大小相匹配,这有助于减少磁盘i/o次数,降低了访问外部存储的成本。
而且,由于叶子节点是全集,会把行数据只存储在叶子节点上,非叶子节点只是存储一个用来排序的 key (比如存个 id )。
数据库里是按行组织数据的,而创建索引的时候是针对这一列进行创建。这一行数据内容是比较多的,而一个id内容是比较少的,所以总体来说叶子节点会非常占用空间,非叶子节点则占不了多少空间。这个时候我们就可以把这些不占多少空间的非叶子节点缓存到内存中(硬盘上还是要存这些非叶子节点的,但是当我们进行查询的时候,就可以把这些非叶子节点加载到内存中。整体查询的比较过程就可以在内存中进行了,这样又进一步减少了io访问次数)。
2. 范围查询性能:b+树以其有序性而著称,这使得范围查询(例如,查询特定范围内的数据)非常高效。由于叶子节点之间通过链表连接,可以轻松遍历叶子节点以获取范围内的数据,而不需要进行额外的排序操作。
3. 高度平衡:b+树的多路性和节点的度数通常较高,这有助于保持整棵树的平衡。高度平衡意味着查询性能在不同数据规模下都能够保持稳定,这对于大规模数据集非常重要。
4. 时间开销稳定:b+树所有的查询最终都是要落到叶子节点上的,查询和查询之间的时间开销是稳定的,不会出现这次特别快,下次特别慢的情况。
对于b树来说,要查询的元素如果在根节点或者层次比较高的节点,就能非常快的拿到元素;但是如果要查的元素在叶子节点,此时花的时间就会更多,所以它是不稳定的。而对于b+树来说,所有的查询最终都是要落到叶子节点上进行查询的,无论查询哪个元素,整体的时间开销都是差不多的,它是稳定的。
5. 有序性:b+树的有序性使得它非常适合支持按键排序的操作,而不需要额外的排序操作。这对于数据库索引等应用非常重要。
6. 联机事务处理:在数据库系统中,b+树索引对于支持联机事务处理和高并发读写操作非常有效。它可以提供快速的等值查询和范围查询,同时允许高效地插入和删除操作。
总的来说,b+树是一种在大规模数据存储和检索方面表现出色的数据结构,特别适用于数据库系统和文件系统等应用。它的平衡性、有序性和高度优化的设计使得它成为了许多关系型数据库管理系统(rdbms)的首选索引结构,确保了高性能和高可用性。
其他索引
mysql支持多种存储引擎,而不仅仅是b+树实现。mysql内部有一个模块叫“存储引擎”,它提供了很多版本的实现。存储引擎是mysql架构中的一个关键组件,它负责管理数据的存储、检索和维护。不同的存储引擎采用不同的数据结构和算法,以适应不同的应用场景和性能需求。
以下是一些常见的mysql存储引擎以及它们的特点:
-
innodb:最常用到的mysql存储引擎是innodb,作为mysql的默认存储引擎,它支持事务处理、行级锁、外键约束等高级功能。innodb的数据存储和索引都是基于b+树的结构。
-
myisam:myisam是一种较早的存储引擎,它不支持事务处理和行级锁,但对于读密集型操作有良好的性能。它的索引也是基于b+树。
-
memory:也称为heap存储引擎,将数据存储在内存中,适用于需要快速读写临时数据的场景。不过,数据在服务器重启时会丢失。
-
ndb cluster:这是一个集群存储引擎,用于构建高可用性和高扩展性的mysql集群。它支持分布式存储和事务处理。
-
tokudb:tokudb是一个高性能的存储引擎,特别适用于大规模数据的写入和高压缩率的需求。它使用了一种不同于b+树的索引结构。
-
rocksdb:rocksdb是一个基于键值对的存储引擎,最初由facebook开发。它适用于高度可定制的应用程序,可以处理大量写入操作。
每个存储引擎都有其优势和限制,选择合适的存储引擎取决于应用程序的需求。管理员可以根据性能、数据完整性、并发性等因素选择合适的存储引擎,甚至在同一数据库中使用不同的存储引擎来满足不同的需求。这使得mysql成为一个非常灵活和适应性强的数据库管理系统。
事务
当谈到事务时,通常是指一组相关的操作,这些操作一起执行以完成特定的任务或功能。
事务的特点在于,其中的每个操作单元要么全部成功,要么全部失败,没有中间状态。
事务的概念不仅适用于数据库管理系统,还可以在各种不同的环境中找到应用。
为什么使用事务
开发中经常会涉及到一些场景,需要我们“一气呵成”地完成一些操作。
让我来解释一下为什么事务很重要。
现在你考虑一个银行转账的情况:
假设你要从你的银行账户a中转账100元到你朋友的账户b中。
这涉及两个关键操作:从a中扣除100美元,然后将100元存入b中。
如果没有事务,以下情况可能发生:
-
扣款成功,但存款失败:如果在扣款后,但在存款前发生故障(程序崩溃、数据库崩溃、机器断电等),那么你的账户a将减少100美元,但朋友的账户b没有增加100美元,这将导致数据不一致。
-
存款成功,但扣款失败:同样,如果在存款后,但在扣款前发生故障,朋友的账户b将增加100美元,但你的账户a没有减少100美元,同样也会导致数据不一致。
但是使用事务,我们就可以将扣款和存款sql操作包装在一个事务中。
如果任何一个操作失败,整个事务将回滚,恢复到初始状态,即扣款和存款都没有发生。
这确保了不会出现数据不一致的情况,因为要么两个操作都成功,要么都不执行(不是真的一个都不执行,必须得先执行才能知道失败与否,只是看起来好像一个都没执行一样)。
这是事务的原子性和一致性特性的体现。
相信你已经感受到事务的意义了,那么我们来真正地认识一下它~~
事务的概念
事务是一种抽象概念,它描述了一组相关的操作,这些操作一起被视为一个不可分割的单元。这意味着在事务中的每个操作都被视为原子操作,要么全部成功,要么全部失败,不存在部分成功或部分失败的情况。这种原子性确保了数据的一致性和完整性。
数据库中的事务:
数据库管理中,事务是一组数据库操作,它们被视为一个独立的工作单元。这些操作可以包括数据的插入、更新、删除等。数据库事务的目标是维护数据的完整性和一致性,即使在发生故障或错误的情况下也要保持数据的正确性。
事务的关键属性:
原子性(atomicity):
原子性是事务的一个重要属性,它确保了事务中的操作要么全部成功,要么全部失败。如果在事务执行过程中发生任何错误,所有已执行的操作将被回滚,数据库状态恢复到事务开始前的状态,从而避免了数据不一致性。
一致性(consistency):
一致性要求事务执行前后,数据库必须保持一致性状态。这意味着事务执行不会违反数据库的完整性规则或约束条件。如果违反了一致性要求,整个事务将被回滚,以确保数据库的一致性。
隔离性(isolation):
隔离性定义了不同事务之间的互相隔离程度。事务之间应该相互隔离,以防止互相干扰。数据库系统提供不同的隔离级别,例如读未提交、读已提交、可重复读和串行化,用于控制事务之间的可见性和互动程度。两个事务之间的影响越大,隔离性就越低,影响越小,隔离性就越高。
这是数据库并发执行多个事务的时候涉及到的问题。mysql是一个客户端服务器结构的程序,一个服务器可以给多个客户端提供服务,多个客户端都会让数据库执行事务,很有可能客户端1提交的事务1才执行了一半,客户端2提交的事务2也过来了。数据库服务器就需要同时处理这两个事务,也就是并发执行。mysql其实就是通过多线程的方式解决并发执行的。
数据库并发执行多个事务的时候,涉及到的问题:
如果我们希望数据库服务器执行效率高,就希望提高并发程度。但是提高了并发程度之后可能会存在一些问题,导致数据出现一些“错误”情况。
隔离级别解释在“数据正确”和“效率”之间做权衡。往往提升了效率就会牺牲正确性,提升了正确性就会牺牲效率。
会出现哪些问题呢?
1、脏读问题:一个事务a正在写数据的过程中,另一个数据b读取了同一个数据。接下来事务a又修改了数据,导致事务b之前读到的数据是一个无效的数据/过时的数据(也称为“脏数据”)。
解决脏读问题,核心思路就是针对写操作加锁。
之前两个事务完全是并发执行的,现在约定加锁之后并发程度就降低了,效率降低了,但是读到的数据的准确性提高了、隔离性提高了。
2、不可重复读问题:并发执行事务过程中,如果事务a在内部多次读取同一个数据的时候出现了不同的情况,就是不可重复读。这是因为在事务a两次读数据之间,有一个事务b修改了数据并提交了事务。
解决不可重复读,需要给读操作加锁。
并发程度进一步降低,隔离性也进一步提高,效率降低了,数据的准确性又提高了。
3、幻读:一个事务a正在执行过程中,两次的读取操作数据内容虽然没变,但是结果集变了。
这个情况是不是问题,具体场景需具体分析。
我们可以引入串行化的方式解决幻读。保持绝对的串行执行事务,此时完全没有并发了。
相当于“釜底抽薪”,从根本上解决了并发中涉及的各个问题。此时并发程度最低(没有并发),隔离性最高,效率最低,数据最精确。
总之,对于效率和准确性的选择与权衡,不同的需求场景有不同的要求。
mysql服务器也提供了“隔离级别”让我们针对隔离程度进行设置,以应对不同的需求场景。我们可以直接在mysql配置文件中修改数据库隔离级别。
mysql一共提供了四种隔离级别,分别对应到上面的三种问题:
-
read uncommitted(读未提交): 这是最低隔离级别,允许一个事务读取另一个事务尚未提交的数据。这可以导致脏读、不可重复读和幻读问题。这个级别通常不推荐在生产环境中使用,因为它的隔离性较低,可能导致数据不一致。它并发程度最高,速度最快,隔离性最低,准确性最低。
-
read committed(读已提交): 这是mysql的默认隔离级别。它确保一个事务只能读取到已经提交的数据,从而解决了脏读问题。但它仍然允许不可重复读和幻读问题的出现。它引入了写加锁,并发程度降低了,速度降低了,隔离性提高了,准确性也提高了。
-
repeatable read(可重复读)(默认级别): 这个隔离级别通过锁定读取的行,确保一个事务在其生命周期内看到一致的数据。它可以解决脏读和不可重复读问题,但仍然可能出现幻读问题。它引入了写加锁和读加锁,并发程度进一步的降低了,速度进一步的降低了,隔离性进一步的提高了,准确性也进一步的提高了。
-
serializable(串行化): 这是最高的隔离级别,它确保事务之间没有并发操作,从而完全消除了脏读、不可重复读和幻读问题。但它的性能开销最高,因为它限制了并发性。它严格的按照串行的方式一个一个的执行事务,并发程度最低,速度最慢,隔离性最高,准确性最高。
持久性(durability):
持久性确保一旦事务成功提交,其结果将永久保存在硬盘中,即使系统发生崩溃或重启,数据仍然存在,事务执行的修改仍然是有效的。这意味着已提交的事务不会丢失,数据将在系统恢复后仍然可用。
事务处理的典型流程
-
开启事务(start transaction): 事务的第一步是明确地开启它。单独执行的每一个sql都是自成一个体系的。此时这些sql之间是没有原子性的。开启事务后的每条sql都会被当作一个整体,直到遇到“commit”结束标记。在大多数数据库管理系统中,使用 start transaction;或 begin;语句来启动一个新事务。一旦事务开始,后续的sql操作将被视为一个原子单元,要么全部成功,要么全部失败。
-
执行多条sql语句: 在事务内部,可以执行多条sql语句,这些语句通常用于修改数据库中的数据或执行其他操作。这些操作将被视为一个逻辑单元,要么一起成功,要么一起失败。在这个阶段,我们可以执行插入、更新、删除等操作。sql里也能支持条件判断、循环。
-
回滚或提交(rollback/commit): 一旦执行了一系列sql语句,我们就可以根据情况选择回滚事务或提交事务。
-
回滚(rollback): 如果在事务执行期间发生了错误,或者你希望撤消之前的操作,可以使用 rollback; 语句来回滚事务,这里是你主动触发的。这像是“翻新”,是事务执行的关键操作。回滚将导致事务内的所有操作都被撤销,数据库状态回到事务开始前的状态。这确保了数据的一致性,避免了潜在的数据损坏。rollback一般是要搭配一些条件判断逻辑来使用的,虽然也支持条件判断、循环等等操作,但是我们日常开发不太会这么写,更多的是搭配其它的编程语言使用。
-
提交(commit): 如果在事务执行期间没有发生错误,而且你希望将所有操作永久应用到数据库中,可以使用commit;语句来提交事务。提交后,事务内的所有操作将变为永久性的,不可逆转。这确保了数据的持久性。
-
使用事务的主要优点是,它们提供了一种机制,可以确保一组相关操作的一致性和完整性,无论是否出现故障或错误。如果在事务执行期间发生问题,都可以回滚事务,避免了对数据库的不良影响。如果一切正常,就可以提交事务,将所有更改永久应用到数据库中。
需要注意的是,虽然不同的数据库管理系统可能具有稍微不同的语法和操作方式,但通用的概念和原则仍然适用于事务处理。
mysql中索引和事务的优点和缺点:
优点:
-
提高查询性能:索引可以显著提高查询的速度,使得数据库可以更快速地检索到所需的数据,这对于读取密集型应用非常有益。
-
保持数据完整性:通过在表上创建唯一索引,可以确保某些列或组合列的数值是唯一的,从而维护数据的完整性。
-
支持连接操作:索引可以加速连接查询,尤其是在大型数据表上进行连接操作时,可以显著提高查询性能。
缺点:
-
占用额外存储空间:索引本身需要占用磁盘空间,这可能在大型数据库中占用相当大的存储空间。
-
增加写操作的开销:插入、更新和删除操作需要更新相关索引,这会增加写操作的时间。特别是在有多个索引的情况下,写操作开销可能显著增加。
-
维护和管理复杂性:随着数据库的变化,索引需要定期维护和重新构建,以确保其性能。这需要额外的管理工作。
与事务相关的注意事项:
-
事务冲突:当多个事务同时操作数据表时,索引可能引发事务冲突。因为事务在更新数据时会锁定相应的行或页,这可能导致其他事务等待。
-
锁定级别:索引的使用也与事务的锁定级别有关。较高的锁定级别(如serializable)可能需要在查询期间锁定更多的行,以确保一致性,但这可能会降低并发性能。
-
事务回滚:如果事务在索引操作期间回滚,可能需要额外的工作来恢复索引的一致性状态。
总的来说,索引在mysql中可以提高查询性能,但需要权衡其优点和缺点,并与事务管理结合考虑,以确保数据库的性能和一致性。
发表评论