事务及其ACID属性
事务是由一组SQL语句组成的逻辑处理单元,事务具有以下4个属性,通常简称为事务的ACID属性。
- 原子性(Atomicity) :事务是一个原子操作单元,其对数据的修改,要么全都执行,要么全都不执行。
- 一致性(Consistent) :在事务开始和完成时,数据都必须保持一致状态。这意味着所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性。
- 隔离性(Isolation) :数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的“独立”环境执行。这意味着事务处理过程中的中间状态对外部是不可见的,反之亦然。
- 持久性(Durable) :事务完成之后,它对于数据的修改是永久性的,即使出现系统故障也能够保持。
通俗来讲,事务就是件事儿,事儿就必须得有始有终,要么就不全做,这就像化学中的原子一样,不可分割。比如,我给我老婆(假设我有)转520元就是个事务,那么我的账户得-520元,而我老婆账户是不是得+520元,你不能给我的账户-520元,我老婆的账户却没加钱。这就是事务原子性的通俗理解。
再来说说事务的一致性:还是刚刚的那个给老婆转账的例子,当我看到我账户少了520元的时候,我老婆也看到她账户多了520元,嗯,这很一致。咦,有人就疑惑了,这和那个原子性好像没啥子区别啊。
其实一致性的含义是要么看到我还没有给我老婆转账的状态,要么我已经成功转账给我老婆的状态,而对于我少了520元,我老婆还没加上520元这个中间状态是不可见的。
原子性关注状态,要么全部成功,要么全部失败,不存在部分成功的状态。而一致性关注数据的可见性,中间状态的数据对外部不可见,只有最初状态和最终状态的数据对外可见。
并发事务处理带来的问题
更新丢失(Lost Update)或脏写:当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题,最后的更新覆盖了由其他事务所做的更新。
脏读(Dirty Reads):一个事务读取另外一个事务还没有提交的数据叫脏读。
例如:事务A修改了某个表中的一行数据,但是还没有提交,这时候事务B读取了被事务A修改后的数据,之后事务A因为某种原因回滚(Rollback)了,那么事务T2读取的数据就是脏的(无效的)。
不可重读(Non-Repeatable Reads):在同一个事务内,两次相同的查询返回了不同的结果。
例如:事务A会读取两次数据,在第一次读取某一条数据后,事务B修改了该数据并提交了事务,A此时再次读取该数据,两次读取便得到了不同的结果。
幻读(Phantom Reads):一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读”。
例如:事务A读取到了事务B提交的新增数据,这不符合事务的隔离性。
不可重复读与幻读的区别
不可重复读针对的是值的不同,幻读指的是数据条数的不同。不可重复读出现多是因为修改;幻读重点是新增、删除。
事务隔离级别
“脏读”、“不可重复读”和“幻读”,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制 来解决。
数据库的事务隔离越严格,并发副作用越小,但付出的代价也就越大,因为事务隔离实质上就是使事务在一定程度上“串行化”进行,这显然与“并发”是矛盾的。同时,不同的应用对读一致性和事务隔离程度的要求也是不同的,比如许多应用对“不可重复读"和“幻读”并不敏感,可能更关心数据并发访问的能力。
设置事务隔离级别:set tx_isolation=‘REPEATABLE-READ’;
查看事务隔离级别:show tx_isolation like ‘REPEATABLE-READ’;
MySql默认隔离级别为Repeatable read;
锁机制
锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中除了计算资源的争用之外,数据也是一种共享的资源。为了保证数据并发访问的一致性、有效性,这个锁就至关重要了。
锁分类
1.性能上划分:乐观锁和悲观锁。
2.对数据库的操作类型划分:读锁和写锁(都是悲观锁)。
3.对数据库的操作粒度划分:表锁和行锁。
乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
悲观锁:顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
读锁(共享锁,S锁):针对同一份数据,多个读操作可以同时进行而不会互相影响。
写锁(排它锁,X锁):当前写操作没有完成前,它会阻断其他写锁和读锁。
表锁
每次操作锁住整张表。开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低; 一般用在整表数据迁移的场景。
行锁
每次操作锁住一行数据。开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度最高。