mysql常用存储引擎以及悲观锁与乐观锁

一.mysql常用存储引擎

(1)MyISAM存储引擎:
不支持事务,也不支持外键,优势是访问速度快,对事务完整性没有要求或者以选择,插入的为主应用基本上可以用这个引擎来创建³³表
可以配合锁,实现操作系统下的复制备份,迁移。 
使用表级锁,并发性差。 
支持全文检索(MySQL InonoDB在5.6以后也支持全文
检索)。主机宕机后,MyISAM表易损坏,灾难恢复性不佳。 
无事务支持。 
只缓存索引,数据的缓存是利用操作系统缓冲区来实现的
。数据紧凑存储,因此可获得更小的索引和更快的全表扫描性能。 

(2)InnoDB存储引擎(从mysql 5.5版本开始,innodb是mysql默认的存储引擎。)
支持事务,灾难恢复性好;支持外键.;支持热备份 
支持事务,默认的事务隔离级别是可重复读,其事务控制是通过多版本并发控制来实现的。 
使用行级锁 
对于InnoDB引擎中的表,其数据的物理组织形式是簇表(Cluster Table),数据按主键来组织,也就是说主键索引数据表的另一种常见形式是非簇表,其索引是有序的,而数据是无序的。 
实现了缓冲管理,不仅能缓冲索引也能缓冲数据,并且会自动创建散列索引以加快数据的获取。相比之下,MyISAM数据只是缓存了索引。 

(3)存储器存储引擎

存储器存储引擎使用存在于内存中的内容来创建表。每个存储器表只实际对应一个磁盘文件,格式是.frm.memory类型的表访问非常的快,因为它的数据是放在内存中的,并且默认使用HASH索引,但是一旦服务关闭,表中的数据就会丢失掉。记忆体 
存储引擎的表可以选择使用BTREE索引或者HASH索引,两种不同类型的索引有其不同的使用范围

Hash索引优点: 
Hash索引结构的特殊性,其检索效率非常高,索引的检索可以一次定位,不像B-Tree索引需要从根节点到枝节点,最后才能访问到页节点这样多次的IO访问,所以散列索引的查询效率要远高于B树索引。 
哈希索引缺点:那么不精确查找呢,也很明显,因为哈希算法是基于等值计算的,所以对于“喜欢”等范围查找哈希索引无效,不支持;

MEMORY存储引擎提供“内存”表,也不支持事务,外键。 
使用内存表(内存引擎)可以显着提高访问数据的速度,可用于缓存会频繁访问的,可以重构的数据,计算结果,统计值,中间结果,但也有如下这些不足之处。 
使用的是表级锁,虽然内存访问快,但如果频繁地读读,表级锁可能会成为瓶颈所在。
只支持固定大小的行.VARCHAR类型的字段会存储为固定长度的CHAR类型,
浪费空间。不支持TEXT,BLOB字段。当有些查询需要使用到临时表(使用的也是MEMORY存储引擎)时如果表中有TEXT,BLOB字段,那么会转化为基于磁盘的MyISAM表,严重降低性能。 
由于内存资源成本昂贵,一般不建议设置过大的内存表,如果内存表满了,就会在MySQL错误日志里发现类似“Thetable'table_name'已满“这样的错误,可通过清除数据或调整内存表参数来避免报错。 
服器重启数据会丢失

(4)MERGE引擎存储
合并存储引擎的英文一组的MyISAM的表的组合,这些的MyISAM的表必须结构完全相同,合并表本身并没有数据,对合并类型的表可以进行查询,更新,删除操作,这些操作实际上是对内部的的MyISAM数据表进行的。

(5)ARCHIVE引擎 
存储引擎的英文被设计用来存储企业中的大量流水数据存储引擎。存档引擎使用的zlib无损数据压缩,让数据都保存在压缩的存储表中。当数据被插入时,它们被压缩。它只支持INSERT和SELECT,支持自增键及其上的索引,不支持其他索引。它适合做日志记录,用户行为分析,不需要更新,删除和索引的数据

二.悲观锁与乐观锁
(1)悲观锁(Pessimistic Lock)
悲观锁的特点是先获取锁,再进行业务操作,即“悲观”的认为获取锁是非常有可能失败的,因此要先确保获取锁成功再进行业务操作。通常所说的“一锁二查三更新”即指的是使用悲观锁。通常来讲在数据库上的悲观锁需要数据库本身提供支持,即通过常用的select … for update操作来实现悲观锁。当数据库执行select for update时会获取被select中的数据行的行锁,因此其他并发执行的select for update如果试图选中同一行则会发生排斥(需要等待行锁被释放),因此达到锁的效果。select for update获取的行锁会在当前事务结束时自动释放,因此必须在事务中使用。

这里需要注意的一点是不同的数据库对select for update的实现和支持都是有所区别的,例如oracle支持select for update no wait,表示如果拿不到锁立刻报错,而不是等待,mysql就没有no wait这个选项。另外mysql还有个问题是select for update语句执行中所有扫描过的行都会被锁上,这一点很容易造成问题。因此如果在mysql中用悲观锁务必要确定走了索引,而不是全表扫描。

(2)乐观锁(Optimistic Lock)
乐观锁的特点先进行业务操作,不到万不得已不去拿锁。即“乐观”的认为拿锁多半是会成功的,因此在进行完业务操作需要实际更新数据的最后一步再去拿一下锁就好。

乐观锁在数据库上的实现完全是逻辑的,不需要数据库提供特殊的支持。一般的做法是在需要锁的数据上增加一个版本号,或者时间戳,然后按照如下方式实现:

复制代码
1. SELECT data AS old_data, version AS old_version FROM …;
2. 根据获取的数据进行业务操作,得到new_data和new_version
3. UPDATE SET data = new_data, version = new_version WHERE version = old_version
if (updated row > 0) {
    // 乐观锁获取成功,操作完成
} else {
    // 乐观锁获取失败,回滚并重试
}

乐观锁是否在事务中其实都是无所谓的,其底层机制是这样:在数据库内部update同一行的时候是不允许并发的,即数据库每次执行一条update语句时会获取被update行的写锁,直到这一行被成功更新后才释放。因此在业务操作进行前获取需要锁的数据的当前版本号,然后实际更新数据时再次对比版本号确认与之前获取的相同,并更新版本号,即可确认这之间没有发生并发的修改。如果更新失败即可认为老版本的数据已经被并发修改掉而不存在了,此时认为获取锁失败,需要回滚整个业务操作并可根据需要重试整个过程。

总结
乐观锁在不发生取锁失败的情况下开销比悲观锁小,但是一旦发生失败回滚开销则比较大,因此适合用在取锁失败概率比较小的场景,可以提升系统并发性能

乐观锁还适用于一些比较特殊的场景,例如在业务操作过程中无法和数据库保持连接等悲观锁无法适用的地方


版权声明:本文为stand_forever原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。