事务及其属性
事务是由一组SQL语句组成的逻辑处理单元,具有以下4个属性,通常简称位ACID属性:
一致性(Consistent):在事务开始和完成时,数据必须保持一致状态。这意味这所有相关的数据规则都必须用于事务的修改,以保证数据的完整性。
原子性(Atomicity):事务是一个原子操作单元,其对数据的修改要么是全部执行要么全部不执行。
隔离性(Isolation):数据库系统提供一定的隔离机制,保证事务在不受外部并行操作影响的独立环境执行。这意味着事务处理过程中的中间状态对外部是不可见的。
持久性(Durable):事务完成后,对于数据的修改是永久性的,即使出现系统故障也能保持。
并发事务的影响
更新丢失(Lost update)或脏写
当两个或多个事务执行同一行时,都是基于最初获取到的数据更新该行,由于每个事务不知道其它事务的存在,就会发生丢失更新问题-最后的更新会覆盖由其它事务所做的更新。
脏读(dirty reads)
一个事务在对一条记录进行修改时,在这个事务完成并提交前,这条记录数据就处于不一致的状态。这时有另一个事务也来读取同一记录,如果不加以控制,读的事务读取了这些数据,但写的数据被回滚,并据此作进一步处理,就会产生未提交的数据依赖关系。
不可重读(non-repeatable reads)
事务a内部的相同查询语句在不同时刻读出的结果不一致
幻读(phantom reads)
事务A读取到了事务B提交的新数据,不符合隔离性。
事务隔离级别
上述问题可用数据库提供一定的事务隔离机制来解决
隔离基本 | 脏读(dirty read) | 不可重复读(non-repeatable read) | 幻读(phantom read) |
读未提交(read uncommitted | 可能 | 可能 | 可能 |
读已提交(read committed) | 不可能 | 可能 | 可能 |
可重复读(repeatable read) | 不可能 | 不可能 | 可能 |
可串行化(serializable) | 不可能 | 不可能 | 不可能 |
MySQL默认的事务级别时可重复读,用spring开发时,如果不设置级别默认使用MySQL设置的,如果spring设置了就使用已设置的隔离级别。
锁
在数据库中除了传统的计算资源的竞争外,数据也是一种用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。
锁分类
从性能上分乐观锁(用版本对比来实现)、悲观锁
从对数据库操作类型分为读锁、写锁。都属于悲观锁
读锁:共享锁。针对同一份数据,多个读操作可以同时进行而互不影响
写锁:排它锁。当前写操作未完成前它会阻断其他写锁和读锁
从对数据操作粒度分为行锁和表锁
表锁
每次操作都对整张表进行加锁。开销小、加锁快,锁粒度大,发生锁冲突概率最高,并发度最低。一般用于数据迁移场景。
手动增加表锁:lock table 表名称 read(write),表名称2 read(write)
查看表上加过的锁: show open tables;
删除表锁: unlock tables;
行锁
每次操作锁住一行数据。开销大,加锁慢,会出现死锁,锁粒度最小,发生锁冲突概率最低,并发度最高。
InnoDB和MYISAM不同点:InnoDB支持事务及行级锁。
总结:MyISAM在执行查询语句select前,会自动给涉及的表加读锁,在执行CUD时会加写锁。InnoDB在执行select时,因为有mvcc机制不会加锁,但CUD会加行锁。简而言之:读锁会阻塞写,但不阻塞读。而写锁会阻塞读和写。
间隙锁(gap lock)
锁的是两个值之间的间隙。MySQL默认的隔离级别是repeatable read,是没有解决幻读的问题,而间隙锁可以在某些场景下可以解决幻读的问题。如有下表:
此表的间隙就有id为(3,20),(20,152),(155,正无穷)这三个区间。当在执行查询条件为id > 10 and id < 100时,其他的session在这个范围所包含的所有行记录里(包括间隙行记录)已经行记录所在间隙插入或修改数据的,即在区间(3,152]区间内做新增修改操作的,注意这个区间是包含152的。
临键锁(Next-key Locks)
临键锁是间隙锁与行锁的组合,像上例的(3,152]区间可以叫临键锁。
无索引行锁会升级为表锁
锁主要是加在索引上的,如果对非索引字段更新,行锁可能会变为表锁。InnoDB的行锁是针对索引加的不是针对记录加的锁,并且该索引不能失效,否则都会从行锁升级为表锁。
结论
Innodb存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面所带来的性能损耗可能比表级锁定会要更高一下,但是在整体并发处理能力方面要远远优于MYISAM的表级锁定的。当系统并发量高的时候,Innodb的整体性能和MYISAM相比就会有比较明显的优势了。 但是,Innodb的行级锁定同样也有其脆弱的一面,当我们使用不当的时候,可能会让Innodb的整体性能表现不仅不能比MYISAM高,甚至可能会更差。
锁优化建议
1、尽可能所有的检索都通过索引来完成,避免无索引行锁升级为表锁
2、合理设计索引,缩小行锁范围
3、尽量减少范围检索,避免间隙锁
4、尽量控制事务大小,减少锁定资源量和时间长度,涉及事务加锁的语句尽量放事务的最后执行
5、尽量低级别事务隔离