欢迎光临
我一直在奋斗

浅谈MySQL常用储存引擎

MySQL储存引擎是MySQL体系架构中的重要组成部分,也是MySQL结构中的核心部分,插件式的储存引擎更是它区别于其它数据库的重要特征,它处于MySQL体系架构中Server端底层,是底层物理结构的实现,用于将数据以各种不同的技术方式存储到文件或者内存中,不同的存储引擎具备不同的存储机制、索引技巧和锁定水平。常见的MySQL储存引擎有,InnoDB,MyISAM,Memory,Archive,他们具备各自的特征,我们可以根据不同的具体应用来建立对应的储存引擎表。

在谈储存引擎之前,我们需要先理解几个基本概念:

一、事务:

事务是一组原子性的SQL语句或者说是一个独立的运行单元,如果数据库引擎能成功对数据库应用这组SQL语句,要么就执行,那么就因为一条语句奔溃或者是其他原因无法执行,那么所有的语句都不会执行。也就是说事务中的语句,要么全部执行,要么全部不执行(执行失败,需要回滚)。

还是那个经典的银行转账例子:

假设银行的数据库有两张表:支票表和储蓄表,现在某个客户A要从其支票账户转移2000元到其储蓄账户,那么至少需求三个步骤:

a.检查A的支票账户余额高于2000元;

b.从A的支票账户余额中减去2000元;

c.在A的储蓄账户余额中增加2000元。

这三个操作必须要放在一个事务中,任何一个步骤失败,则必须要回滚所有的步骤,否则A作为银行的客户就可能要莫名损失2000元,就出问题了。这就是一个典型的事务,这个事务是不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,不可能只执行其中一部分,这也是事务的原子性特征。

上面只说了事务的原子性,除了原子性,事务还有隔离性,一致性和持久性。

1、原子性:事务中的操作要么都执行,要么都不执行。

2、隔离性:事务与事务之间可以相互影响的程度,隔离性主要用于数据的并发访问控制。事务隔离性从低到高分为read uncommitted(读未提交),read committed(读已提交),Repeatable read(可重复读),serializable。当事务的隔离级别为read uncommitted时可能发生脏读,不可重复读和幻读的情况,当事务的隔离级别为read committed时可能发生不可重复读和幻读,当事务的隔离级别为Repeatable read时可能会发生幻读,当事务的隔离级别为serializable时可以完全杜绝以上的脏读,不可重复读及幻读情况,下面解释下脏读,不可重复读及幻读的含义:

(1)脏读:一个事务可以读取到另外一个还未提交的事务修改的数据,这样造成的影响是如果这个事务回滚了那么另外一个事务看到的就是一笔脏数据。

(2)不可重复读:一个事务在执行过程中,针对同一笔数据的读取结果都不一样,如果事务1在事务2更新之前读取过一次,在事务2更新之后又读取过一次,那么将导致两次读取的结果不一样,这样要注意不可重复读指的是事务中操作的同一笔数据,不是数据集。

(3)幻读:一个事务在执行过程中,多次进行查询操作,但是每次查询的结果集都是不一样的,幻读针对的是多笔记录。

大多数据库默认的数据隔离级别为read commited。

3、一致性:一致性要求事务所包含的操作不能违反数据资源的一致性检查,数据资源在事务执行之前处于某个数据一致性状态,那么事务执行之后也需要保持数据之间的一致性状态。我们先说的那个银行转账的列子就是为了保持数据一致性,我们使用事务的其中一个目的就是为了避免这种不一致的情况产生。

4、持久性:当事务提交之后所有的修改操作都不能再进行任何的修改(数据库系统一般是通过冗余储存或者多数据网络备份等方式来保证数据的一致性)。

二、读锁和写锁:

无论何时,只要有多个SQL需要同一时刻修改数据,都会产生并发控制的问题。

假设一个公共邮箱,用户A正在读取邮箱,同时,用户B正在删除邮箱中的某个邮件,会产生什么结果呢?客户A可能读取时会报错退出,也可能会读取到不一致的邮箱数据。如果把邮箱当作数据库中的一张表,可见其存在同样的问题。

解决这类经典问题的方法就是并发控制,即在处理并发读或者写时,可以通过实现一个由两种类型的锁组成的锁系统来解决问题。这两种锁就是共享锁和排他锁,也叫读锁和写锁(java中读写锁可以使用JUC中ReentrantReadWriteLock来实现,其内部包含一个读锁和一个写锁,底层使用的是一个volatile 类型的int变量,高16位用作读锁的状态,后16位用作写锁的状态)。

读锁是共享的,即相互不阻塞的,多个客户在同一时刻可以读取同一资源,互不干扰。写锁是排他的,即一个写锁会阻塞其它的写锁和读锁,只有这样,才能确保给定时间内,只有一个用户能执行写入,防止其它用户读取正在写入的同一资源。写锁优先级高于读锁。

三、行锁和表锁(java中线程安全的HashTable或通过Collections.synchronizedMap得到的HashMap和ConcurrentHashMap就类似于这里的表锁和行锁):

实际数据库系统中每时每刻都在发生锁定,锁也是有粒度的,提高共享资源并发行的方式就是让锁更有选择性,尽量只锁定需要修改的部分数据,而不是所有的资源,因此要进行精确的锁定。但是由于加锁也需要消耗资源,包括获得锁、检查锁是否解除、释放锁等,都会增加系统的开销。所谓的锁策略就是要在锁的开销和数据的安全性之间寻求平衡,这种平衡也会影响性能。

每种MySQL存储引擎都有自己的锁策略和锁粒度,最常用的两种重要的锁策略分别是表锁和行锁。

表锁是开销最小的策略,会锁定整张表,用户对表做写操作时,要先获得写锁,这会阻塞其它用户对该表的所有读写操作。没有写锁时,其它读取的用户才能获得读锁,读锁之间是不相互阻塞的。行锁可以最大程度支持并发处理,但也带来了最大的锁开销,它只对指定的记录加锁,其它进程还是可以对同一表中的其它记录进行操作。表级锁速度快,但冲突多,行级锁冲突少,但速度慢(每次访问数据都需要加锁)。

理解了上面几个概念,我们就可以很好地分辨不同存储引擎之间的区别了。

一、InnoDB储存引擎:

MySQL存储引擎可以分为官方存储引擎和第三方存储引擎,InnoDB就是强大的第三方存储引擎,具备较好的性能和自动崩溃恢复特性,目前应用极为广泛,是当前MySQL存储引擎中的主流,它在事务型存储和非事务型存储中都很流行。

InnoDB存储引擎支持事务、支持行锁、支持非锁定读、支持外键。这里提一下非锁定读(采用读写分离的思想,读和写时访问的是不同的资源):一致性非锁定读(consistent nonlocking read)是指InnoDB存储引擎通过多版本控制(multi versionning)的方式来读取当前执行时间数据库中行的数据,如果读取的行正在执行DELETE或UPDATE操作,这时读取操作不会因此等待行上锁的释放。相反的,InnoDB会去读取行的一个快照数据(一个历史数据),该机制极大地提高了数据库的并发性。(有关非锁定读的详情可参看这篇博客https://www.cnblogs.com/olinux/p/5174888.html)

如非特别原因,应用建表时都可以首选考虑使用InnoDB。

二、MyISAM储存引擎:

MyISAM存储引擎是MySQL官方提供的存储引擎,它在InnoDB出现并完善之前是MySQL存储引擎的主流,但目前逐渐被淘汰主要因为其不支持事务,这或许源于MySQL的开发者认为不是所有的应用都需要事务,所以便存在了这种不支持事务的存储引擎。

MyISAM不支持事务,不支持行级锁,支持表锁,支持全文索引(MySQL5.6之后InnoDB引擎也支持全文索引了),最大的缺陷是崩溃后无法安全恢复。

MyISAM因设计简单,数据以紧密格式存储,所以某些场景下性能很好,但是它的表锁又带来了性能问题,如果你发现所有的查询都长期处于“Locked”状态,表锁就是罪魁祸首了。

因此,对于只读数据,或者表比较小,可以忍受修复操作的可以依然使用MyISAM,对于不需要事务的应用,选择MyISAM存储引擎,或许可以获得更高的性能,MySQL自带的默认的information_schema库中就存在使用MyISAM存储引擎的表。

三、Memory储存引擎:

Memory存储引擎将表中数据放在内存中,因此速度非常快,但因其只支持表锁,所以并发性能较差,最糟糕的是这个存储引擎在数据库重启或崩溃之后表中的数据将全部丢失,它只适用于存储临时数据的临时表,MySQL中一般使用这个存储引擎来存放查询的中间结果集,如MySQL自带的默认的information_schema库中就存在较多使用Memory存储引擎的表。

四、Archive储存引擎:

Archive存储引擎置只支持INSERT和SELECT操作,支持行锁,但本身并不是事务安全的存储引擎,其最大的优点是其具有较好的压缩比,压缩比一般可达到1:10,可以将同样的数据以更小的磁盘空间占用来存储。

Archive存储引擎非常适合存储归档数据,如历史数据、日志信息数据等等,这类数据往往数据量非常大,并且基本只有INSERT和SELECT操作,使用这个存储引擎可以非常节约磁盘空间。

其它存储引擎使用较少,这里就不谈了。

参考资料:《Spring揭秘》 http://blog.csdn.net/hangxing_2015/article/details/52585979

未经允许不得转载:奋斗者的足迹 » 浅谈MySQL常用储存引擎
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

奋斗者的足迹

联系我们加入我们