• 锁定和阻塞


      锁是事务获取的一种控制资源,用于保护数据资源,防止其它事务对数据进行冲突的或不兼容的访问。我们目前只要学习两种基本的锁模式就可以,它们分别是共享锁和排他锁。

    共享锁主要是在读操作时使用,读操作一旦完成,数据库就会立即释放资源上的共享锁,而且多个事务可以同时持有同一数据资源上的共享锁。而排他锁主要是在修改数据时使用,一旦授予,事务将一直持有排他锁,直到事务完成(提交或回滚)。对于同一数据资源,如果有其他事务已经获取了该资源的任何类型的锁,就不能再获取该资源的排他锁,如果有其他事务已经获得了该资源的排他锁,就不能再获取该资源的任何类型的锁。

      下面用一个示例来对其进行演示。首先开启一个会话 Connection 1,打开一个事务,对Production.Products表的一行进行更新,为产品2的当前单价19.00增加1.00,为了修改这一行,会话必须先获得一个排他锁。如下代码。

    USE TSQLFundamentals2008;
    GO
    
    -- Connection 1,修改數據獲取排他鎖
    BEGIN TRAN --開啟事務,但沒有commit,所以該會話會一直持有排他鎖
    
    UPDATE Production.Products SET unitprice+=1.00
    WHERE productid=2;

    因为这个事务没有完成(没有提交或回滚),所以该会话会一直持有排他锁。此时再开启一个会话Connection 2,试图去查询这一行数据,为了读操作这个会话需要一个共享锁,但是这一行已经被前面那个会话的排他锁锁定,而且共享锁和排他锁是不兼容的,所以会话Connection 2会被阻塞,进入等待状态。如下代码。

    USE TSQLFundamentals2008;
    GO
    
    -- Connection 2,查詢數據獲取共享鎖
    -- 因為這一行已經被會話Connection 1所持有的排他鎖鎖定,而排他鎖和共享鎖是不兼容的,所以該會話會被阻塞
    SELECT * FROM Production.Products
    WHERE productid=2;

    如果发生了这样的锁定和阻塞,我们就会想办法去分析和排除这种阻塞,为此,我们可以通过一些动态管理对象来得到关于锁的详细信息。如下代码。

    USE TSQLFundamentals2008;
    GO
    
    -- Connection 3
    -- 1,通過動態管理視圖sys.dm_tran_locks查看該數據庫阻塞鏈中進程的信息
    SELECT request_session_id AS spid, --會話ID
            resource_type AS restype, -- 資源類型
            resource_database_id AS dbid, --數據庫ID
            DB_NAME(resource_database_id) AS dbname, --數據庫名稱
            resource_description AS res,--資源描述
            resource_associated_entity_id AS resid,-- 資源相關聯實體的ID
            request_mode AS mode,--鎖模式
            request_status AS STATUS --鎖狀態
    FROM sys.dm_tran_locks;
    
    -- 2,通過動態管理視圖sys.dm_exec_connections查看阻塞鏈中進程關聯的聯接信息
    -- 比如联接建立的时间,最后一次发生读操作和写操作的时间以及最后执行的SQL代码
    SELECT session_id AS spid,
            connect_time AS connecttime,
            last_read,
            last_write,
            text
    FROM sys.dm_exec_connections
    CROSS APPLY sys.dm_exec_sql_text(most_recent_sql_handle) AS ST
    WHERE session_id IN(52,54);
    
    -- 3,通过动态管理视图sys.dm_exec_sessions找到更多有用的信息
    -- 包括登录的用户名,主机名和登录时间,最后请求开始时间和最后请求结束时间
    SELECT session_id AS spid,
            login_time,
            host_name,
            program_name,
            nt_user_name,
            last_request_start_time,
            last_request_end_time
    FROM sys.dm_exec_sessions
    WHERE session_id IN(52,54);
    
    -- 4,可通過動態管理視圖sys.dm_exec_requests查詢到導致阻塞的進程的詳細信息
    SELECT session_id AS spid,
            blocking_session_id,
            command,
            sql_handle,
            database_id,
            wait_type,
            wait_time,
            wait_resource
    FROM sys.dm_exec_requests
    WHERE blocking_session_id>0;
    
    -- 利用KILL<spid>命令終止導致阻塞的進程
    -- 該操作會導致Connection 1中的事務回滾
    KILL 51;
  • 相关阅读:
    面试题1:赋值运算符函数
    面试题:寻找热门查询
    面试题9:斐波那契数列
    Java中的volatile关键字
    二分查找算法
    面试题8:旋转数组的最小数字
    面试题:在O(1)空间复杂度范围内对一个数组中前后连段有序数组进行归并排序
    百度面试题:从海量日志中提取访问百度次数最多的IP
    面试总结
    java垃圾回收
  • 原文地址:https://www.cnblogs.com/mcgrady/p/4031637.html
Copyright © 2020-2023  润新知