数据库中的锁:
锁的种类:
按照锁的内容上分为:表锁,页锁,行锁。InnoDB支持行锁,MyIsam支持表锁,BerkeleyDBA支持页锁。不同的粒度
按照锁的性质分可以分为:共享锁(读锁或S锁),独占锁(写锁,排它锁)(X锁),更新锁(U锁)(意向锁)
当执行select时候,加共享锁,当insert update delete等操作时,加排它锁。更新锁首先对数据对象作更新锁锁定,这样数据将不能被修改,但可以读取。等到SQL Server确定要进行更新数据操作时,他会自动将更新锁换为独占锁,当对象上有其他锁存在时,无法对其加更新锁。
意向锁是为了提高封锁子系统的效率。该封锁子系统支持多种封锁粒度。原因是:在多粒度封锁方法中一个数据对象可能以两种方式加锁 ― 显式封锁和隐式封锁。
数据库引擎使用意向锁来保护锁层次结构的底层资源,以防止其他事务对自己锁住的资源造成伤害,提高锁冲突检测性能。例如,当读取表里的页面时,在请求页共享锁(S锁)之前,事务在表级请求共享意向锁。这样可以防止其他事务随后在表上获取排他锁(X锁),修改整个表格。意向锁可以提高性能,因为数据库引擎仅在表级检查意向锁,确定事务是否能安全地获取该表上的锁,而不需要检查表中的每行或每页上的锁以确定事务是否可以锁定整个表。
悲观锁与乐观锁
1、悲观锁
正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
2、乐观锁( Optimistic Locking )
相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。
而乐观锁机制在一定程度上解决了这个问题。乐观锁,大多是基于数据版本( Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。
事务
数据库事务(Transaction)是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。一方面,当多个应用程序并发访问数据库时,事务可以在应用程序间提供一个隔离方法,防止互相干扰。另一方面,事务为数据库操作序列提供了一个从失败恢复正常的方法。
事务的四个特性
事务具有四个特性:原子性(Atomicity)、一致性(Consistency)、隔离型(Isolation)、持久性(Durability),简称ACID。
原子性(Atomicity) 事务的原子性是指事务中的操作不可拆分,只允许全部执行或者全部不执行。
一致性(Consistency) 事务的一致性是指事务的执行不能破坏数据库的一致性,一致性也称为完整性。一个事务在执行后,数据库必须从一个一致性状态转变为另一个一致性状态。
隔离性(Isolation) 事务的隔离型是指并发的事务相互隔离,不能互相干扰。
持久性(Durability) 事务的持久性是指事务一旦提交,对数据的状态变更应该被永久保存。
数据库隔离级别
对于同时运行的多个事务,当这些事务访问数据库中相同的数据时,如果没有采取必要的隔离机制,就会导致各种并发问题:
脏读:对于两个事务T1,T2,T1读取了已经被T2更新但还没有提交的字段,之后,若T2回滚,T1读取到的内容就是临时无效的内容。
不可重复读:对于事务T1,T2,T1需要读取一个字段两次,在第一次和第二次读取之间,T2更新了该字段,导致T1第二次读取到的内容值不同。
幻读: 事务A读取与搜索条件相匹配的若干行。事务B以插入或删除行等方式来修改事务A的结果集,然后再提交。 幻读与不可重复读之间的区别是幻读强调的是新增或删除,而不可重复读强调的是修改。比如Mary两次查工资,中间有人改过工资,则两次结果不一样,这就是不可重复读。Mary要查工资一千的人数,第一次查到了10个,中间有人增加了一条工资为一千的人,下次查的时候就变成了11个,好像第一次查询的是幻觉一样。
事务的四个隔离级别 实际工作中事务几乎都是并发的,完全做到互相之间不干扰会严重牺牲性能,为了平衡隔离型和性能,SQL92规范定义了四个事务隔离级别:读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)、串行化(Serializable)。四个级别逐渐增强,每个级别解决上个级别的一个问题。
读未提交(Read Uncommitted) 另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据(脏读)。 脏读是指另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据。
读已提交(Read Committed) 本事务读取到的是最新的数据(其他事务提交后的)。问题是,在同一个事务里,前后两次相同的SELECT会读到不同的结果(不可重复读)。
不可重复读是指同一个事务执行过程中,另外一个事务提交了新数据,因此本事务先后两次读到的数据结果会不一致。
可重复读(Repeatable Read) 在同一个事务里,SELECT的结果是事务开始时间点的状态,同样的SELECT操作读到的结果会是一致的。但是,会有幻读现象。
可重复读保证了同一个事务里,查询的结果都是事务开始时的状态(一致性)。但是,如果另一个事务同时提交了新数据,本事务再更新时,就会发现了这些新数据,貌似之前读到的数据是幻觉,这就是幻读。
串行化(Serializable) 所有事务只能一个接一个串行执行,不能并发。
隔离级别的选择
事务隔离级别越高,越能保证数据的一致性,但对并发性能影响越大,一致性和高性能必须有所取舍或折中。
一般情况下,多数应用程序可以选择将数据库的隔离级别设置为读已提交,这样可以避免脏读,也可以得到不错的并发性能。尽管这个隔离级别会导致不可重复度、幻读,但这种个别场合应用程序可以通过主动加锁进行并发控制。
Oracle支持两种隔离级别,READ COMMITED和SERIALIZABLE默认的事务隔离级别是READ COMMITED
MYSQL支持4中隔离界别,默认的是REPEATED READ