简述mysql存储引擎及读写锁实现并发访问控制
一、Mysql的存储引擎
存储引擎是数据库的核心组件,其工作于数据库底层。不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能。使用不同的存储引擎,还可以获得特定的功能。MySQL提供了多个不同的存储引擎,包括处理事务安全的引擎和处理非事务安全表的引擎。在MySQL中,不需要在整个服务器中都使用同一种存储引擎,可针对每一个表使用不同的存储引擎,但是不建议这样做。
在MySQL中可通过show engines
和show table status
分别查看系统支持的引擎类型及相关数据表所使用的存储引擎。常见的存储引擎有InnoDB、MyISAM、Memory、Archive等等。
1、InnoDB存储引擎
InnoDB是强大的由第三方提供的存储引擎,具有较好的性能和自动崩溃恢复的特性,是目前MySQL存储引擎中的主流。其支持事务、行锁、非锁定读以及外键。InnoDB适合在对事务要求较高的场景中,适用于处理大量短期事务(并发高)。其数据存储于表空间中,可以将数据库中所有类型为InnoDB的表的数据和索引存储在同一个表空间文件中,也可以根据每一个表生成一个独立的表空间文件。
2、MyISAM存储引擎
MyISAM存储引擎是MySQL官方提供的存储引擎,其是在InnoDB出现并完善之前的存储引擎主流。MyISAM不支持事务,不支持行级锁,支持表锁、全文索引,但是其最大的缺陷在于崩溃后无法安全恢复。MyISAM适用于只读或读写少的应用场景,在高并发的场景因表锁的存在,其读写有可能会长期处于Locked的状态。
3、Memory存储引擎
Memory存储引擎将表中数据放在内存中,因此速度非常快,但因其支持表锁,所以并发性能较差,最糟糕的是这个存储引擎在数据库重启或崩溃之后表中的数据将全部丢失,它只适用于存储临时数据的临时表,MySQL中一般使用这个存储引擎来存放查询的中间结果集
4、Archive存储引擎
Archive存储引擎置只支持INSERT和SELECT操作,支持行锁,但本身并不是事务安全的存储引擎,其最大的优点是其具有较好的压缩比,压缩比一般可达到1:10,可以将同样的数据以更小的磁盘空间占用来存储。Archive存储引擎非常适合存储归档数据,如历史数据、日志信息数据等等,这类数据往往数据量非常大,并且基本只有INSERT和SELECT操作,使用这个存储引擎可以非常节约磁盘空间。
二、锁与并发控制及事务
上面在介绍Mysql存储引擎时,提到了锁和事务这两个概念,这两个概念与实现Mysql的并发控制息息相关。
1、锁
无论何时,只要有多个SQL需要同一时刻修改数据,都会产生并发控制的问题。解决这类问题的方法就是并发控制,即在处理并发读或者写时,可以通过实现一个由两种类型的锁组成的锁系统来解决问题。这两种锁就是共享锁和排他锁,也叫读锁和写锁。其分别表示:
共享锁:是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。
排他锁:排他锁也称为写锁,一个写锁会阻塞其他的写锁或读锁,保证同一时刻只有一个连接可以写入数据,同时防止其他用户对这个数据的读写。
实际上数据库系统中每时每刻都在发生锁定,锁也是有粒度的,提高共享资源并发行的方式就是让锁更有选择性,尽量只锁定需要修改的部分数据,而不是所有的资源,因此要进行精确的锁定。但是由于加锁也需要消耗资源,包括获得锁、检查锁是否解除、释放锁等,都会增加系统的开销。所谓的锁策略就是要在锁的开销和数据的安全性之间寻求平衡,这种平衡也会影响性能。 每种MySQL存储引擎都有自己的锁策略和锁粒度,最常用的两种重要的锁策略分别是表锁和行锁。
表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。一般ddl使用。表锁分为表共享读锁(共享锁)和表独占写锁(排他锁)。
行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。行级锁分为共享锁和排他锁。
锁的使用方式有下面三种
1)表级锁
LOCK TABLES tbl_name read|write, tbl_name read|write, ...
其解锁方式为:UNLOCK TABLES;
2)在刷新表缓存时添加读/写锁
FLUSH TABLES tbl_name,... [WITH READ LOCK];
其解锁方式为:UNLOCK TABLES;
3)行级锁,对选择的行添加读或写锁
SELECT cluase [FOR UPDATE | LOCK IN SHARE MODE];
其解锁方式为:COMMIT; 或者ROLLBACK;
2、事务
事务是一组原子性的SQL语句或者说是一个独立的工作单元,如果数据库引擎能够成功对数据库应用这组SQL语句,那么就执行,如果其中有任何一条语句因为崩溃或其它原因无法执行,那么所有的语句都不会执行。也就是说,事务内的语句,要么全部执行成功,要么全部执行失败。
事务具有ACID特性,即:
A:AUTOMICITY,原子性,一个事务必须保证其中的操作要么全部执行,要么全部回滚,不可能存在只执行了一部分这种情况出现。
C:CONSISTENCY,一致性;数据库总是应该从一个一致性状态转为另一个一致性状态,如fallback时,修改了的数据应一起回退到原始状态,而不会出现某些数据没有回滚的情况。
I:ISOLATION,隔离性;一个事务所做出的操作在提交之前,是否能为其它事务可见。通常来说在一个事务未执行完毕时,系统会保证其他Session 无法看到这个事务的执行结果。
D:DURABILITY,持久性;事务一旦提交,其所做出的修改会永久保存;
为了保证并发操作,事务提供了多个隔离级别来适应不同的应用环境,如:
事务的隔离级别READ UNCOMMITTED:事务中的修改,即使没有提交,对其他会话也是可见的, 即可以读取未提交的数据——脏读。
READ COMMITTED:这个隔离级别保证了一个事务如果没有完全成功(commit执行完),事务中的操作对其他会话是不可见的。
REPEATABLE READ:这个隔离级别解决了脏读的问题,幻读问题。这里指的是innodb的rr级别,innodb中使用next-key锁对”当前读”进行加锁,锁住行以及可能产生幻读的插入位置,阻止新的数据插入产生幻行。
SERIALIZABLE:最严的隔离级别,通过给事务中每次读取的行加锁,写加写锁,保证不产生幻读问题,但是会导致大量超时以及锁争用问题。
事务的管理与启动可通过下面几个Mysql命令进行操作:
启动事务:START TRANSACTION
提交事务:COMMIT
将事务回滚到启动状态:ROLLBACK
配置savepoint:SAVEPOINT identifier
回滚到指定的savepoint:ROLLBACK [WORK] TO [SAVEPOINT] identifier
释放savepoint:RELEASE SAVEPOINT identifier
mysql默认采用AutoCommit模式,也就是每个sql都是一个事务,并不需要显示的执行事务。 如果autoCommit关闭,那么每个sql都默认开启一个事务,只有显式的执行“commit”后这个事务才会被提交。
#可通过下面两个命令来查看autoCommit的状态
MariaDB [hidb]> SELECT @@autocommit;
MariaDB [hidb]> show variables like 'autocommit';
MariaDB [hidb]> set autocommit=0; #0表示AutoCommit关闭
MariaDB [hidb]> set autocommit=1; #1表示AutoCommit开启
三、配置示例
事务与行级锁示例:
配置示例1
配置示例2