锁:是数据库引擎用来同步多个用户同时对一个数据块访问的一种机制。
锁的粒度和层次结构
数据库引擎具有多粒度锁定,允许一个事务锁定不同类型的资源。
锁定在较小的粒度(例如行)可以提高并发度,但开销大,因为锁定的范围越小,需要的锁就越多(锁定了许多行,就需要持有更多的锁);锁定在较大的粒度(例如表)会降低并发度,但是消耗较低,因为锁定的范围大,需要的锁就越小(锁定了表,限制了其他事物对表的访问)。
数据库引擎通常必须获取多粒度级别上的锁才能完整地保护资源。这种多粒度级别上的锁称为锁层次结构
可锁定的资源:
锁模式:
数据库引擎使用不同的锁模式锁定资源,这些锁模式确定了并发事务访问资源的方式。
共享锁(S) :读取产生的锁,防止其他事务对其的修改
更新锁(U) :可防止死锁,在共享锁与排他锁之间,与共享锁兼容,与排他锁不兼容。
排他锁(X) :用于数据增删改操作。锁定时数据无法修改与读取(在nolock或未提交读隔离级别时可以脏读)
意向锁(I) :意向共享锁(IS),意向排他(IX),意向更新(IU),共享意向更新(SIU),更新意向排他(UIX)
架构所(Sch-):架构稳定性锁(Sch-M),架构稳定性锁(Sch-S)
大容量更新锁(BU):当将数据大容量复制到表,且指定TABLEOCK提示或者使用sp_tableoption设置了table lockon bulk表选项时,将使用大容量更新锁。大容量更新锁允许多个线程将数据并发地大容量加载到同一表,同时防止其他不进行大容量加载数据的进程访问该表。
锁关键字
NOLOCK:不锁定任何资源(仅在SELECT语句中使用)
ROWLOCK:行锁
PAGELOCK:页锁
TABLOCK:表共享锁,其他事务可以读取表,但不能更新删除
TABLOCKX:表独占锁,其他事务不能读取表,更新和删除
UPDATELOCK : 表提示指定在读取的每一行上设置一个更新锁定,直到整个语句或事物执行完毕,才释放这个锁定。允许其他事务获取同一行的共享锁定,但不允许他们更新行
XLOCK:指定才用排他锁并保持到事务完成。
HOLDLOCK:保持表锁定直到事务执行完毕。
并发控制类型
1. 悲观锁并发控制:用到锁来保护数据。用于锁消耗低于回滚事务的成本环境中;(相信并发是绝大部分的,并且每一个线程都必须要达到目的)
2. 乐观锁并发控制:并发过程中不产生锁,读取数据后检查用户数据,判断是否产生错误, 回滚事务。用于数据争用少的环境。(相信并发是极少数的,假设运气不好遇到了,就放弃并返回信息告诉它再次尝试。因为它是极少数发生的。)
悲观锁一定成功,但在并发量特别大的时候会造成很长堵塞甚至超时,仅适合小并发的情况。
乐观锁不一定每次都修改成功,但能充分利用系统的并发处理机制,在大并发量的时候效率要高很多。
并发可能导致的影响
1. 丢失更新:多个事务同时更新同一行;
2. 脏读:修改过程中的数据被读取;
3. 不一致的分析(不可重复读):读取的事务里面,符合读取条件的行搜索条件被更改了;(Update)
4. 幻读:读取的事务里面,符合读取条件的行被删除或者插入了;(Insert.Delete)