为了避免上面出现的几种情况,在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同。
也称为读未提交(Read Uncommitted):允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个数据则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。
授权读取
读提交(Read Committed):允许不可重复读取,但不允许脏读取。这可以通过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
可重复读取
可重复读取(Repeatable Read):禁止不可重复读取和脏读取,但是有时可能出现幻影数据。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。
序列化
序列化(Serializable):提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed。它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、虚读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。
共享锁(S锁):如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁,直到已释放所有共享锁。获准共享锁的事务只能读数据,不能修改数据。
排他锁(X锁):如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的锁,直到在事务的末尾将资源上的锁释放为止。获准排他锁的事务既能读数据,又能修改数据。
linux 自旋锁:
当进程进入CPU运行时,就会给它的代码上锁,以免别的CPU中的进程修改里面的代码(不排除CPU给别的CPU上锁这样的情况,以后会讨论到。)。所谓子旋锁就是这样的一把锁:进程A进入CPU,锁上门运行,进程B来到CPU前,发现门被锁上了,于是等待进程A出来交出开锁钥匙。
正如每次我们谈到“锁”这个概念 时,总会谈到“死锁”——是的,我们用锁,就必须防止死锁,死锁是这样产生的:进程A进入CPU运行,上锁,进程B 来到CPU门前等待进程A出来,可是糟糕的情况出现了:进程A要想出来就必须获取进程B的帮助,于是进程A开始等待进程B的帮助,可是进程B却又一直等待 进程A出来!这样的等待无法终止,最终成为死锁。
再 比如,进程A要锁上甲代码段,然后想再去锁乙代码段,进程B要锁上乙代码段,然后想再去锁甲代码段。第一步大家都没问题,可是两个进程都要进行下一步时, 发现无法完成任务了:进程A已经锁上甲代码段,进程B没法再去操作它,同理进程B已经锁上乙代码段,进程A也没办法操作它,于是两个进程等待对方释放锁, 当然,这样的等待也是无止无休的。这就好象两辆汽车在一座很榨的桥上相向行驶,两车碰头谁也不让谁,都在等待对方让路。
避免死锁,必须使每次上锁操作都是有顺序的、原子的操作。有顺序的,也就是说每次都按照可执行队列地址从低向高的顺序上锁——我们以后会很好的讨论这个。
原子的,就是说每次上锁必须执行到底,否则不予执行
两种锁的加锁原理
互斥锁:线程会从sleep(加锁)——>running(解锁),过程中有上下文的切换,cpu的抢占,信号的发送等开销。
自旋锁:线程一直是running(加锁——>解锁),死循环检测锁的标志位,机制不复杂。
两种锁的区别
互斥锁的起始原始开销要高于自旋锁,但是基本是一劳永逸,临界区持锁时间的大小并不会对互斥锁的开销造成影响,而自旋锁是死循环检测,加锁全程消耗cpu,起始开销虽然低于互斥锁,但是随着持锁时间,加锁的开销是线性增长。
两种锁的应用
互斥锁用于临界区持锁时间比较长的操作,比如下面这些情况都可以考虑
1 临界区有IO操作
2 临界区代码复杂或者循环量大
3 临界区竞争非常激烈
4 单核处理器
至于自旋锁就主要用在临界区持锁时间非常短且CPU资源不紧张的情况下。
自旋-互斥锁
下面的英文介绍了混合互斥锁和混合自旋锁,但是不管是第一段说的先上非阻塞锁后上阻塞锁,还是第二段说的先自旋上锁后进行休眠,反正思路都是先自旋上锁一定时间后在上互斥锁,这种自旋-互斥锁适合各线程持锁时间间隔跨度比较大的情况。
共享锁(S锁):如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁,直到已释放所有共享锁。获准共享锁的事务只能读数据,不能修改数据。
排他锁(X锁):如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的锁,直到在事务的末尾将资源上的锁释放为止。获准排他锁的事务既能读数据,又能修改数据。
linux 自旋锁:
当进程进入CPU运行时,就会给它的代码上锁,以免别的CPU中的进程修改里面的代码(不排除CPU给别的CPU上锁这样的情况,以后会讨论到。)。所谓子旋锁就是这样的一把锁:进程A进入CPU,锁上门运行,进程B来到CPU前,发现门被锁上了,于是等待进程A出来交出开锁钥匙。
正如每次我们谈到“锁”这个概念 时,总会谈到“死锁”——是的,我们用锁,就必须防止死锁,死锁是这样产生的:进程A进入CPU运行,上锁,进程B 来到CPU门前等待进程A出来,可是糟糕的情况出现了:进程A要想出来就必须获取进程B的帮助,于是进程A开始等待进程B的帮助,可是进程B却又一直等待 进程A出来!这样的等待无法终止,最终成为死锁。
再 比如,进程A要锁上甲代码段,然后想再去锁乙代码段,进程B要锁上乙代码段,然后想再去锁甲代码段。第一步大家都没问题,可是两个进程都要进行下一步时, 发现无法完成任务了:进程A已经锁上甲代码段,进程B没法再去操作它,同理进程B已经锁上乙代码段,进程A也没办法操作它,于是两个进程等待对方释放锁, 当然,这样的等待也是无止无休的。这就好象两辆汽车在一座很榨的桥上相向行驶,两车碰头谁也不让谁,都在等待对方让路。
避免死锁,必须使每次上锁操作都是有顺序的、原子的操作。有顺序的,也就是说每次都按照可执行队列地址从低向高的顺序上锁——我们以后会很好的讨论这个。
原子的,就是说每次上锁必须执行到底,否则不予执行
两种锁的加锁原理
互斥锁:线程会从sleep(加锁)——>running(解锁),过程中有上下文的切换,cpu的抢占,信号的发送等开销。
自旋锁:线程一直是running(加锁——>解锁),死循环检测锁的标志位,机制不复杂。
两种锁的区别
互斥锁的起始原始开销要高于自旋锁,但是基本是一劳永逸,临界区持锁时间的大小并不会对互斥锁的开销造成影响,而自旋锁是死循环检测,加锁全程消耗cpu,起始开销虽然低于互斥锁,但是随着持锁时间,加锁的开销是线性增长。
两种锁的应用
互斥锁用于临界区持锁时间比较长的操作,比如下面这些情况都可以考虑
1 临界区有IO操作
2 临界区代码复杂或者循环量大
3 临界区竞争非常激烈
4 单核处理器
至于自旋锁就主要用在临界区持锁时间非常短且CPU资源不紧张的情况下。
自旋-互斥锁
下面的英文介绍了混合互斥锁和混合自旋锁,但是不管是第一段说的先上非阻塞锁后上阻塞锁,还是第二段说的先自旋上锁后进行休眠,反正思路都是先自旋上锁一定时间后在上互斥锁,这种自旋-互斥锁适合各线程持锁时间间隔跨度比较大的情况。
参考博文:http://m.blog.chinaunix.net/uid-26602509-id-3306451.html