第二十二节: 以SQLServer为例介绍数据库自有的锁机制(共享锁、更新锁、排它锁等)和事务隔离级别 :

一. 基本概念

1.共享锁:(holdlock)

 (1). select的时候会自动加上共享锁,该条语句执行完,共享锁立即释放,与事务是否提交没有关系。

 (2). 显式通过添加(holdlock)来显式添加共享锁(比如给select语句显式添加共享锁),当在事务里的时候,需要事务结束,该共享锁才能释放。

 (3). 同一资源,共享锁和排它锁不能共存,意味着update之前必须等资源上的共享锁释放后才能进行。

 (4). 共享锁和共享锁可以共存在一个资源上,意味着同一个资源允许多个线程同时进行select。

2. 排它锁:(xlock)

 (1). update(或 insert 或 delete)的时候加自动加上排它锁,该条语句执行完,排它锁立即释放,如果有事务的话,需要事务提交,该排它锁才能释放。

 (2). 显式的通过添加(xlock)来显式的添加排它锁(比如给select语句显式添加排它锁),如果有事务的话,需要事务提交,该排它锁才能释放。

 (2). 同一资源,共享锁和排它锁不能共存,意味着update之前必须等资源上的共享锁释放后才能进行。

3. 更新锁:(updlock)

 (1). 更新锁只能显式的通过(updlock)来添加,当在事务里的时候,需要事务结束,该更新锁才能释放。

 (2). 共享锁和更新锁可以同时在同一个资源上,即加了更新锁,其他线程仍然可以进行select。

 (3). 更新锁和更新锁不能共存(同一时间同一资源上不能存在两个更新锁)。

 (4). 更新锁和排它锁不兼容

 (5). 利用更新锁来解决死锁问题,要比xlock性能高一些,因为加了updlock后,其他线程是可以进行select的。

4. 意向锁

 意向锁分为三种:意向共享 (IS)、意向排他 (IX) 和意向排他共享 (SIX)。 意向锁可以提高性能,因为数据库引擎仅在表级检查意向锁来确定事务是否可以安全地获取该表上的锁,而不需要检查表中的每行或每页上的锁以确定事务是否可以锁定整个表.

 T1:select * from table (xlock) where id=10

 T2:select * from table (tablock) 

分析:T1线程执行该语句时,会对该表id=10的这一行加排他锁,同时会对整个表加上意向排它锁(IX),当T2执行的时候,不需要逐条去检查资源,只需要看到该表已经存在【意向排它锁】,就直接等待。

PS: update table set xx=xx where id=1, 不光会对id=1的这条记录加排它锁,还会对整张表加意向排它锁。

5. 计划锁(Schema Locks)

 用jdbc向数据库发送了一条新的sql语句,数据库要先对之进行编译,在编译期间,也会加锁,称之为:计划锁。

 编译这条语句过程中,其它线程可以对表做任何操作(update、delete、加排他锁等等),但不能做DDL(比如alter table)操作。

6. 锁的颗粒:行锁、页锁、表锁

 (1). rowlock:行锁---对每一行加锁,然后释放。(对某行加共享锁)

 (2). paglock:页锁---1执行时,会先对第一页加锁,读完第一页后,释放锁,再对第二页加锁,依此类推。(对某页加共享锁)

  假设前10行记录恰好是一页(当然,一般不可能一页只有10行记录),那么T1执行到第一页查询时,并不会阻塞T2的更新。

 (3). tablock:表锁---对整个表加锁,然后释放。 (对整张表加共享锁)

注:

  1. 以上三种锁执行完该语句后即可释放,无须等待事务的提交,与事务是否提交没有关系。

  2. 以上三种锁划分的角度不同,都是共享锁,所以他们相互之间是可以共存的。

7. rowlock、paglock、tablock 和 holdlock的区别

 二者无非是划分的角度不同,其实都是共享锁,但在释放上有所不同

 tablock(rowlock、paglock):对表、行、页加共享锁,只要语句执行完,就释放,与事务是否提交没关系。

 holdlock:对表加共享锁,必须等着事务执行完,才能释放。

8. tablockx对表加排它锁,在有事务和没事务的时候的区别

 (1). 无事务的时候:其他线程无法对该表进行读和更新,除非加tablockx的语句执行完,才能进行。

 (2). 有事务的时候:必须整个事务执行了commit或rollback后才会释放该排他锁。

 xlock还可这么用:select * from table(xlock tablock) 效果等同于select * from table(tablockx)

9.各种锁的兼容关系

  

二. 实战测试

1. 测试共享锁和共享锁可以共存

复制代码

 1  {2                 Console.WriteLine("测试共享锁和共享锁可以共存");3                 Console.WriteLine("共享锁select默认添加:1, 共享锁(holdlock)显式添加:2");4                 string num = Console.ReadLine();5                 if (num == "1")6                 {7                     using (LockDemoDBEntities1 db = new LockDemoDBEntities1())8                     using (var tx = db.Database.BeginTransaction())9                     {
10                         Console.WriteLine("开始查询");
11                         //select的时候会加共享锁
12                         var orderInfor = db.Database.SqlQuery<OrderInfor>("select * from OrderInfor where id=2").FirstOrDefault();
13                         Console.WriteLine($"id为:{orderInfor.id},userName为:{orderInfor.userName},destination为:{orderInfor.destination}");
14 
15                         Console.WriteLine("按任意键进行事务提交");
16                         Console.ReadKey();
17                         try
18                         {
19                             tx.Commit();
20                             Console.WriteLine("事务提交成功");
21                         }
22                         catch (Exception ex)
23                         {
24                             tx.Rollback();
25                             Console.WriteLine("事务提交失败");
26                             Console.WriteLine(ex.Message);
27                         }
28                     }
29                 }
30                 else if (num == "2")
31                 {
32                     using (LockDemoDBEntities1 db = new LockDemoDBEntities1())
33                     using (var tx = db.Database.BeginTransaction())
34                     {
35                         Console.WriteLine("开始查询");
36                         //显式的通过holdlock添加
37                         var orderInfor = db.Database.SqlQuery<OrderInfor>("select * from OrderInfor(holdlock) where id=2").FirstOrDefault();
38                         Console.WriteLine($"id为:{orderInfor.id},userName为:{orderInfor.userName},destination为:{orderInfor.destination}");
39 
40                         Console.WriteLine("按任意键进行事务提交");
41                         Console.ReadKey();
42                         try
43                         {
44                             tx.Commit();
45                             Console.WriteLine("事务提交成功");
46                         }
47                         catch (Exception ex)
48                         {
49                             tx.Rollback();
50                             Console.WriteLine("事务提交失败");
51                             Console.WriteLine(ex.Message);
52                         }
53                     }
54                 }
55             }

复制代码

结论:

  默认加 或者 显式(holdlock)的方式加,都能共存。

2. 测试排它锁和排它锁不能共存

复制代码

 1             {2                 Console.WriteLine("测试排它锁和排它锁不能共存");3                 Console.WriteLine("排它锁update默认添加:1,  排它锁(xlock)显式添加:2");4                 string num = Console.ReadLine();5                 if (num == "1")6                 {7                     Console.WriteLine("排它锁业务");8                     using (LockDemoDBEntities1 db = new LockDemoDBEntities1())9                     using (var tx = db.Database.BeginTransaction())
10                     {
11                         Console.WriteLine("开始更新");
12                         //update的时候会自动加排它锁
13                         var result = db.Database.ExecuteSqlCommand("update OrderInfor set userName='lmr2' where id=2");
14                         Console.WriteLine("按任意键进行事务提交");
15                         Console.ReadKey();
16                         try
17                         {
18                             tx.Commit();
19                             Console.WriteLine("事务提交成功");
20                         }
21                         catch (Exception ex)
22                         {
23                             tx.Rollback();
24                             Console.WriteLine("事务提交失败");
25                             Console.WriteLine(ex.Message);
26                         }
27                     }
28 
29                 }
30                 else if (num == "2")
31                 {
32                     using (LockDemoDBEntities1 db = new LockDemoDBEntities1())
33                     using (var tx = db.Database.BeginTransaction())
34                     {
35                         Console.WriteLine("开始查询");
36                         //select的时候会对id=1的那行加上排它锁,同时对整张表加上意向排它锁,当别的线程查询该表时,不用逐条去检查资源
37                         //只需要看到该表存在【意向排它锁】,就会等待,无论查询id等于几,都会等待
38                         var orderInfor = db.Database.SqlQuery<OrderInfor>("select * from OrderInfor (xlock) where id=1").FirstOrDefault();
39                         Console.WriteLine($"id为:{orderInfor.id},userName为:{orderInfor.userName},destination为:{orderInfor.destination}");
40 
41                         Console.WriteLine("按任意键进行事务提交");
42                         Console.ReadKey();
43                         try
44                         {
45                             tx.Commit();
46                             Console.WriteLine("事务提交成功");
47                         }
48                         catch (Exception ex)
49                         {
50                             tx.Rollback();
51                             Console.WriteLine("事务提交失败");
52                             Console.WriteLine(ex.Message);
53                         }
54                     }
55                 }
56 
57             }

复制代码

结论:

  1. 关于排它锁,无论是显式(xlock)模式添加还是update默认加的模式,如果在事务里都需要事务提交才能释放。

  2. 默认与默认、显式与显式、默认与显式 这三种组合关系都不能共存,所以证明排它锁和排它锁之间不能共存。

  3. 注意:这里加排他锁,会在表层次上加上意向排它锁,与操作那条数据无关。

3. 测试共享锁和排它锁不能共存(显式和隐式) (两个结论未完成)

复制代码

  1             {2                 Console.WriteLine("测试排它锁和排它锁不能共存");3                 Console.WriteLine("默认select共享锁:1, 显式共享锁(holdlock):2,默认update排它锁:3,显式排它锁(xlock):4");4                 string num = Console.ReadLine();5 6                 if (num == "1")7                 {8                     using (LockDemoDBEntities1 db = new LockDemoDBEntities1())9                     using (var tx = db.Database.BeginTransaction())10                     {11                         Console.WriteLine("开始查询");12                         //select的时候会加共享锁13                         var orderInfor = db.Database.SqlQuery<OrderInfor>("select * from OrderInfor where id=2").FirstOrDefault();14                         Console.WriteLine($"id为:{orderInfor.id},userName为:{orderInfor.userName},destination为:{orderInfor.destination}");15 16                         Console.WriteLine("按任意键进行事务提交");17                         Console.ReadKey();18                         try19                         {20                             tx.Commit();21                             Console.WriteLine("事务提交成功");22                         }23                         catch (Exception ex)24                         {25                             tx.Rollback();26                             Console.WriteLine("事务提交失败");27                             Console.WriteLine(ex.Message);28                         }29                     }30                 }31                 else if (num == "2")32                 {33                     using (LockDemoDBEntities1 db = new LockDemoDBEntities1())34                     using (var tx = db.Database.BeginTransaction())35                     {36                         Console.WriteLine("开始查询");37                         //显式的通过holdlock添加38                         var orderInfor = db.Database.SqlQuery<OrderInfor>("select * from OrderInfor(holdlock) where id=2").FirstOrDefault();39                         Console.WriteLine($"id为:{orderInfor.id},userName为:{orderInfor.userName},destination为:{orderInfor.destination}");40 41                         Console.WriteLine("按任意键进行事务提交");42                         Console.ReadKey();43                         try44                         {45                             tx.Commit();46                             Console.WriteLine("事务提交成功");47                         }48                         catch (Exception ex)49                         {50                             tx.Rollback();51                             Console.WriteLine("事务提交失败");52                             Console.WriteLine(ex.Message);53                         }54                     }55                 }56                 else if (num == "3")57                 {58                     using (LockDemoDBEntities1 db = new LockDemoDBEntities1())59                     using (var tx = db.Database.BeginTransaction())60                     {61                         Console.WriteLine("开始更新");62                         //update的时候会自动加排它锁63                         var result = db.Database.ExecuteSqlCommand("update OrderInfor set userName='lmr2' where id=2");64                         Console.WriteLine("按任意键进行事务提交");65                         Console.ReadKey();66                         try67                         {68                             tx.Commit();69                             Console.WriteLine("事务提交成功");70                         }71                         catch (Exception ex)72                         {73                             tx.Rollback();74                             Console.WriteLine("事务提交失败");75                             Console.WriteLine(ex.Message);76                         }77                     }78                 }79                 else if (num == "4")80                 {81                     using (LockDemoDBEntities1 db = new LockDemoDBEntities1())82                     using (var tx = db.Database.BeginTransaction())83                     {84                         Console.WriteLine("开始查询");85                         //select的时候会对id=1的那行加上排它锁,同时对整张表加上意向排它锁,当别的线程查询该表时,不用逐条去检查资源86                         //只需要看到该表存在【意向排它锁】,就会等待,无论查询id等于几,都会等待87                         var orderInfor = db.Database.SqlQuery<OrderInfor>("select * from OrderInfor (xlock) where id=1").FirstOrDefault();88                         Console.WriteLine($"id为:{orderInfor.id},userName为:{orderInfor.userName},destination为:{orderInfor.destination}");89 90                         Console.WriteLine("按任意键进行事务提交");91                         Console.ReadKey();92                         try93                         {94                             tx.Commit();95                             Console.WriteLine("事务提交成功");96                         }97                         catch (Exception ex)98                         {99                             tx.Rollback();
100                             Console.WriteLine("事务提交失败");
101                             Console.WriteLine(ex.Message);
102                         }
103                     }
104                 }
105 
106 
107             }

复制代码

结论:

  1.默认select共享锁先执行,未提交事务的情况下,默认update排它锁 和 显式排它锁(xlock)都能正常执行。,

  证明:默认共享锁语句执行完立即释放,与事务是否提交没有关系

  2.显式共享锁(holdlock),未提交事务的情况下,默认update排它锁 和 显式排它锁(xlock)都 不能 正常执行,

  证明:显式共享锁(holdlock)需要事务提交才能释放,同时也证明共享锁和排它锁不能共存。

  3.默认update排它锁先执行,未提交事务的情况下,默认select共享锁能执行,显式共享锁(holdlock)不能执行。

  证明:

  4.显式排它锁(xlock)先执行,未提交事务的情况下,默认select共享锁能执行,显式共享锁(holdlock)不能执行。

  证明:

4. 测试共享锁、更新锁、排它锁间的关系

复制代码

 1             {2                 Console.WriteLine("测试更新锁、排它锁、共享锁间的关系");3                 Console.WriteLine("更新锁业务输入:1, 排它锁业务输入:2,共享锁输入:3");4                 string num = Console.ReadLine();5                 if (num == "1")6                 {7                     Console.WriteLine("更新锁业务");8                     using (LockDemoDBEntities1 db = new LockDemoDBEntities1())9                     using (var tx = db.Database.BeginTransaction())
10                     {
11                         Console.WriteLine("开始查询");
12                         //显式的添加更新锁
13                         var orderInfor = db.Database.SqlQuery<OrderInfor>("select * from OrderInfor(updlock) where id=2").FirstOrDefault();
14                         Console.WriteLine($"id为:{orderInfor.id},userName为:{orderInfor.userName},destination为:{orderInfor.destination}");
15                         Console.WriteLine("按任意键进行事务提交");
16                         Console.ReadKey();
17                         try
18                         {
19                             tx.Commit();
20                             Console.WriteLine("事务提交成功");
21                         }
22                         catch (Exception ex)
23                         {
24                             tx.Rollback();
25                             Console.WriteLine("事务提交失败");
26                             Console.WriteLine(ex.Message);
27                         }
28                     }
29                 }
30                 else if (num == "2")
31                 {
32                     Console.WriteLine("排它锁业务");
33                     using (LockDemoDBEntities1 db = new LockDemoDBEntities1())
34                     using (var tx = db.Database.BeginTransaction())
35                     {
36                         Console.WriteLine("开始更新");
37                         //update的时候会自动加排它锁
38                         var result = db.Database.ExecuteSqlCommand("update OrderInfor set userName='lmr2' where id=2");
39                         Console.WriteLine("按任意键进行事务提交");
40                         Console.ReadKey();
41                         try
42                         {
43                             tx.Commit();
44                             Console.WriteLine("事务提交成功");
45                         }
46                         catch (Exception ex)
47                         {
48                             tx.Rollback();
49                             Console.WriteLine("事务提交失败");
50                             Console.WriteLine(ex.Message);
51                         }
52                     }
53                 }
54                 else if (num == "3")
55                 {
56                     Console.WriteLine("共享锁业务");
57                     using (LockDemoDBEntities1 db = new LockDemoDBEntities1())
58                     using (var tx = db.Database.BeginTransaction())
59                     {
60                         Console.WriteLine("开始查询");
61                         //select的时候会自动加共享锁
62                         var orderInfor = db.Database.SqlQuery<OrderInfor>("select * from OrderInfor where id=2").FirstOrDefault();
63                         Console.WriteLine($"id为:{orderInfor.id},userName为:{orderInfor.userName},destination为:{orderInfor.destination}");
64                         Console.WriteLine("按任意键进行事务提交");
65                         Console.ReadKey();
66                         try
67                         {
68                             tx.Commit();
69                             Console.WriteLine("事务提交成功");
70                         }
71                         catch (Exception ex)
72                         {
73                             tx.Rollback();
74                             Console.WriteLine("事务提交失败");
75                             Console.WriteLine(ex.Message);
76                         }
77                     }
78                 }
79             }

复制代码

结论:

  1. 更新锁需要事务提交才能释放。

  2. 更新锁和更新锁不能共存。

  3. 更新锁和排它锁不能共存。

  4. 更新锁和共享锁可以共存。

5. 测试表锁和排它锁不能共存

复制代码

 1             {2                 Console.WriteLine("测试表锁和排它锁的问题");3                 Console.WriteLine("表锁业务输入:1, 排它锁业务输入:2");4                 string num = Console.ReadLine();5                 if (num == "1")6                 {7                     Console.WriteLine("表锁业务");8                     using (LockDemoDBEntities1 db = new LockDemoDBEntities1())9                     using (var tx = db.Database.BeginTransaction())
10                     {
11                         Console.WriteLine("开始查询");
12 
13                         var orderInfor = db.Database.SqlQuery<OrderInfor>("select * from OrderInfor(tablock) where id=2").FirstOrDefault();
14                         Console.WriteLine($"id为:{orderInfor.id},userName为:{orderInfor.userName},destination为:{orderInfor.destination}");
15                         Console.WriteLine("按任意键进行事务提交");
16                         Console.ReadKey();
17                         try
18                         {
19                             tx.Commit();
20                             Console.WriteLine("事务提交成功");
21                         }
22                         catch (Exception ex)
23                         {
24                             tx.Rollback();
25                             Console.WriteLine("事务提交失败");
26                             Console.WriteLine(ex.Message);
27                         }
28                     }
29                 }
30                 else if (num == "2")
31                 {
32                     Console.WriteLine("排它锁业务");
33                     using (LockDemoDBEntities1 db = new LockDemoDBEntities1())
34                     using (var tx = db.Database.BeginTransaction())
35                     {
36                         Console.WriteLine("开始更新");
37                         //update的时候会自动加排它锁
38                         var result = db.Database.ExecuteSqlCommand("update OrderInfor set userName='lmr2' where id=2");
39                         Console.WriteLine("按任意键进行事务提交");
40                         Console.ReadKey();
41                         try
42                         {
43                             tx.Commit();
44                             Console.WriteLine("事务提交成功");
45                         }
46                         catch (Exception ex)
47                         {
48                             tx.Rollback();
49                             Console.WriteLine("事务提交失败");
50                             Console.WriteLine(ex.Message);
51                         }
52                     }
53                 }
54             }

复制代码

 结论:

  1. 先执行表锁,事务未提交的情况下,排它锁能正常进行。

  证明:表锁只要执行完该语句立即释放,与事务是否提交没有关系。

  2. 先执行默认排它锁,事务未提交的情况想,表锁不能运行。

  证明:默认的排它锁必须等待事务提交完才能释放,同时证明排它锁和表锁不能共存 (表锁在这里的特点和共享锁一样,实质表锁也就是个共享锁,只是划分的角度不同)

6. 测试行锁和排它锁不能共存

复制代码

 1             {2                 Console.WriteLine("测试行锁和排它锁的问题");3                 Console.WriteLine("行锁业务输入:1, 排它锁业务输入:2");4                 string num = Console.ReadLine();5                 if (num == "1")6                 {7                     Console.WriteLine("行锁业务");8                     using (LockDemoDBEntities1 db = new LockDemoDBEntities1())9                     using (var tx = db.Database.BeginTransaction())
10                     {
11                         Console.WriteLine("开始查询");
12 
13                         var orderInfor = db.Database.SqlQuery<OrderInfor>("select * from OrderInfor(rowlock) where id=2").FirstOrDefault();
14                         Console.WriteLine($"id为:{orderInfor.id},userName为:{orderInfor.userName},destination为:{orderInfor.destination}");
15                         Console.WriteLine("按任意键进行事务提交");
16                         Console.ReadKey();
17                         try
18                         {
19                             tx.Commit();
20                             Console.WriteLine("事务提交成功");
21                         }
22                         catch (Exception ex)
23                         {
24                             tx.Rollback();
25                             Console.WriteLine("事务提交失败");
26                             Console.WriteLine(ex.Message);
27                         }
28                     }
29                 }
30                 else if (num == "2")
31                 {
32                     Console.WriteLine("排它锁业务");
33                     using (LockDemoDBEntities1 db = new LockDemoDBEntities1())
34                     using (var tx = db.Database.BeginTransaction())
35                     {
36                         Console.WriteLine("开始更新");
37                         //update的时候会自动加排它锁
38                         var result = db.Database.ExecuteSqlCommand("update OrderInfor set userName='lmr2' where id=1");
39                         Console.WriteLine("按任意键进行事务提交");
40                         Console.ReadKey();
41                         try
42                         {
43                             tx.Commit();
44                             Console.WriteLine("事务提交成功");
45                         }
46                         catch (Exception ex)
47                         {
48                             tx.Rollback();
49                             Console.WriteLine("事务提交失败");
50                             Console.WriteLine(ex.Message);
51                         }
52                     }
53                 }
54             }

复制代码

结论:

  1. 先执行行锁,事务未提交的情况下,排它锁能正常进行。

  证明:行锁只要执行完该语句立即释放,与事务是否提交没有关系。

  2. 先执行默认排它锁,事务未提交的情况想,行锁不能运行。

  证明:默认的排它锁必须等待事务提交完才能释放,同时证明排它锁和行锁不能共存 (行锁在这里的特点和共享锁一样,实质表锁也就是个共享锁,只是划分的角度不同)。

7. 测试页锁和排它锁不能共存(与表锁、行锁类似,不单独测试)

 

三. 事务隔离级别

1. 四种错误

 (1). 脏读:第一个事务读取第二个事务正在更新的数据,如果第二个事务还没有更新完成,那么第一个事务读取的数据将是一半为更新过的,一半还没更新过的数据,这样的数据毫无意义。

 (2). 幻读:第一个事务读取一个结果集后,第二个事务,对这个结果集进行“增删”操作,然而第一个事务中再次对这个结果集进行查询时,数据发现丢失或新增。

 (3). 更新丢失:多个用户同时对一个数据资源进行更新,必定会产生被覆盖的数据,造成数据读写异常。

 (4). 不可重复读:如果一个用户在一个事务中多次读取一条数据,而另外一个用户则同时更新啦这条数据,造成第一个用户多次读取数据不一致。

2. 死锁

(1). 定义:相互等待对方释放资源,造成资源读写拥挤堵塞的情况,就被称为死锁现象,也叫做阻塞。如下面的例子:

1 begin tran
2     select * from OrderInfor(holdlock) where id='333'
3     waitfor  delay '0:0:8' --等待8秒执行下面的语句
4     update OrderInfor set userName='ypf1' where id='333'
5 commit tran

分析:线程T1 和 线程T2 同时执行该事务,假设线程T1先执行完select,线程T2随后执行完select,线程T1要执行update语句的时候,根据数据库策略需要将【共享锁】提升为【排它锁】才能执行,所以必须等线程T2上的【共享锁】释放,而线程T2需要事务提交完才能释放锁,同时T1的【共享锁】不释放导致T2要一直等待,这样造成了T1和T2相互等待的局面,就是死锁现象。

(2). 数据库的默认处理思路的逻辑:

  数据库并不会出现无限等待的情况,是因为数据库搜索引擎会定期检测这种状况,一旦发现有情况,立马【随机】选择一个事务作为牺牲品。牺牲的事务,将会回滚数据。有点像两个人在过独木桥,两个无脑的人都走在啦独木桥中间,如果不落水,必定要有一个人给退回来。这种相互等待的过程,是一种耗时耗资源的现象,所以能避则避。

(3). 手动控制锁级别:

 语法:set deadlock_priority <级别>

 死锁处理的优先级别为 low<normal<high,不指定的情况下默认为normal,牺牲品为随机。如果指定,牺牲品为级别低的。

 还可以使用数字来处理标识级别:-10到-5为low,-5为normal,-5到10为high,数越小,级别越低,越先牺牲,越先回滚。

(4). 案例测试

  事先准备: 使用【LockDemoDB】中的OrderInfor表进行测试, 事先插入一条测试数据,之后都使用该数据进行测试。

1 insert into OrderInfor values('333','ypf','去青岛','lmr','1')

  在两个窗口里(即两个线程)执行下面一段代码:

复制代码

 1 -- 线程1执行下面语句2 begin tran3 begin try4     set deadlock_priority  -95     select * from OrderInfor(holdlock) where id='333'6     waitfor  delay '0:0:8' --等待8秒执行下面的语句7     update OrderInfor set userName='ypf1' where id='333'8     commit tran9 end try
10 begin catch
11     rollback tran
12 end catch

复制代码

复制代码

 1 -- 线程2测试(下面语句单独开一个窗口进行测试)2 begin tran3 begin try4     set deadlock_priority  -85     select * from OrderInfor(holdlock) where id='333'6     waitfor  delay '0:0:8' --等待8秒执行下面的语句7     update OrderInfor set userName='ypf2' where id='333'8     commit tran9 end try
10 begin catch
11     rollback tran
12 end catch

复制代码

分析:线程1和线程2分别执行下面语句,产生死锁,由于线程1设置的级别  -9 < -8,所以线程1牺牲且回滚,最后是线程2执行的结果,userName为ypf2  .

 (5). 扩展补充

 A. 查看锁活动情况

1 select * from sys.dm_tran_locks

B. 查看事务活动情况

1 dbcc opentran

C.  设置锁的超时时间

1 set lock_timeout 4000

PS:

   发生死锁的时候,数据库引擎会自动检测死锁,解决问题,然而这样子是很被动,只能在发生死锁后,等待处理。然而我们也可以主动出击,设置锁超时时间,一旦资源被锁定阻塞,超过设置的锁定时间,阻塞语句自动取消,释放资源,报1222错误。

  好东西一般都具有两面性,调优的同时,也有他的不足之处,那就是一旦超过时间,语句取消,释放资源,但是当前报错事务,不会回滚,会造成数据错误,你需要在程序中捕获1222错误,用程序处理当前事务的逻辑,使数据正确。为0时,即为一旦发现资源锁定,立即报错,不在等待,当前事务不回滚,设置时间需谨慎处理后事啊,你hold不住的。
拓展杀死锁和进程

复制代码

  1 --检测死锁2 --如果发生死锁了,我们怎么去检测具体发生死锁的是哪条SQL语句或存储过程?3 --这时我们可以使用以下存储过程来检测,就可以查出引起死锁的进程和SQL语句。SQL Server自带的系统存储过程sp_who和sp_lock也可以用来查找阻塞和死锁, 但没有这里介绍的方法好用。 4 5 use master6 go7 create procedure sp_who_lock8 as9 begin10 declare @spid int,@bl int,11  @intTransactionCountOnEntry  int,12         @intRowcount    int,13         @intCountProperties   int,14         @intCounter    int15 16  create table #tmp_lock_who (17  id int identity(1,1),18  spid smallint,19  bl smallint)20  21  IF @@ERROR<>0 RETURN @@ERROR22  23  insert into #tmp_lock_who(spid,bl) select  0 ,blocked24    from (select * from sysprocesses where  blocked>0 ) a 25    where not exists(select * from (select * from sysprocesses where  blocked>0 ) b 26    where a.blocked=spid)27    union select spid,blocked from sysprocesses where  blocked>028 29  IF @@ERROR<>0 RETURN @@ERROR 30   31 -- 找到临时表的记录数32  select  @intCountProperties = Count(*),@intCounter = 133  from #tmp_lock_who34  35  IF @@ERROR<>0 RETURN @@ERROR 36  37  if @intCountProperties=038   select '现在没有阻塞和死锁信息' as message39 40 -- 循环开始41 while @intCounter <= @intCountProperties42 begin43 -- 取第一条记录44   select  @spid = spid,@bl = bl45   from #tmp_lock_who where Id = @intCounter 46  begin47   if @spid =0 48             select '引起数据库死锁的是: '+ CAST(@bl AS VARCHAR(10)) + '进程号,其执行的SQL语法如下'49  else50             select '进程号SPID:'+ CAST(@spid AS VARCHAR(10))+ '被' + '进程号SPID:'+ CAST(@bl AS VARCHAR(10)) +'阻塞,其当前进程执行的SQL语法如下'51  DBCC INPUTBUFFER (@bl )52  end 53 54 -- 循环指针下移55  set @intCounter = @intCounter + 156 end57 58 drop table #tmp_lock_who59 60 return 061 end62  63 64 --杀死锁和进程65 --如何去手动的杀死进程和锁?最简单的办法,重新启动服务。但是这里要介绍一个存储过程,通过显式的调用,可以杀死进程和锁。66 67 use master68 go69 70 if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[p_killspid]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)71 drop procedure [dbo].[p_killspid]72 GO73 74 create proc p_killspid75 @dbname varchar(200)    --要关闭进程的数据库名76 as  77     declare @sql  nvarchar(500)  78     declare @spid nvarchar(20)79 80     declare #tb cursor for81         select spid=cast(spid as varchar(20)) from master..sysprocesses where dbid=db_id(@dbname)82     open #tb83     fetch next from #tb into @spid84     while @@fetch_status=085     begin  86         exec('kill '+@spid)87         fetch next from #tb into @spid88     end  89     close #tb90     deallocate #tb91 go92 93 --用法  94 exec p_killspid  'newdbpy' 95 96 --查看锁信息97 --如何查看系统中所有锁的详细信息?在企业管理管理器中,我们可以看到一些进程和锁的信息,这里介绍另外一种方法。98 --查看锁信息99 create table #t(req_spid int,obj_name sysname)
100 
101 declare @s nvarchar(4000)
102     ,@rid int,@dbname sysname,@id int,@objname sysname
103 
104 declare tb cursor for 
105     select distinct req_spid,dbname=db_name(rsc_dbid),rsc_objid
106     from master..syslockinfo where rsc_type in(4,5)
107 open tb
108 fetch next from tb into @rid,@dbname,@id
109 while @@fetch_status=0
110 begin
111     set @s='select @objname=name from ['+@dbname+']..sysobjects where id=@id'
112     exec sp_executesql @s,N'@objname sysname out,@id int',@objname out,@id
113     insert into #t values(@rid,@objname)
114     fetch next from tb into @rid,@dbname,@id
115 end
116 close tb
117 deallocate tb
118 
119 select 进程id=a.req_spid
120     ,数据库=db_name(rsc_dbid)
121     ,类型=case rsc_type when 1 then 'NULL 资源(未使用)'
122         when 2 then '数据库'
123         when 3 then '文件'
124         when 4 then '索引'
125         when 5 then '表'
126         when 6 then '页'
127         when 7 then '键'
128         when 8 then '扩展盘区'
129         when 9 then 'RID(行 ID)'
130         when 10 then '应用程序'
131     end
132     ,对象id=rsc_objid
133     ,对象名=b.obj_name
134     ,rsc_indid
135  from master..syslockinfo a left join #t b on a.req_spid=b.req_spid
136 
137 go
138 drop table #t

复制代码

 3. 事务隔离级别

  read uncommitted:这个隔离级别最低啦,可以读取到一个事务正在处理的数据,但事务还未提交,这种级别的读取叫做脏读。

  read committed:这个级别是默认选项,不能脏读,不能读取事务正在处理没有提交的数据,但能修改。

  repeatable read:不能读取事务正在处理的数据,也不能修改事务处理数据前的数据。

  snapshot:指定事务在开始的时候,就获得了已经提交数据的快照,因此当前事务只能看到事务开始之前对数据所做的修改。

  serializable:最高事务隔离级别,只能看到事务处理之前的数据。 

 事先准备: 使用【LockDemoDB】中的OrderInfor表进行测试, 事先插入一条测试数据,之后都使用该数据进行测试。

线程1执行下面代码:

1  begin tran 
2    update OrderInfor set userName='ypf1' where id='333'
3    waitfor  delay '0:0:8' --等待8秒执行下面的语句
4  rollback tran

 线程1执行后,开启一个新线程(在一个新窗口)马上执行下面代码:

情况1

复制代码

1 --1. 设置允许脏读,能马上读出来数据
2 set tran isolation level read uncommitted
3 select * from OrderInfor where id='333' --读取的数据为正在修改的数据 ,即为脏读
4 
5 --8秒之后数据已经回滚,查出来的数据是回滚后的数据 ypf
6 waitfor  delay '0:0:8'  
7 select * from OrderInfor where id='333'  

复制代码

情况2

复制代码

1 --2. 设置不允许脏读,不能马上读出来数据(数据库默认就是这种模式)
2 set tran isolation level read committed
3 select * from OrderInfor where id='333' 
4 
5 
6 --可以修改(但也得等线程1执行完事务后),8s后显示的是 ypf2,而不是原回滚后的数据ypf
7 update OrderInfor set userName='ypf2' where id='333'   
8 waitfor  delay '0:0:8'  
9 select * from OrderInfor where id='333' 

复制代码

其它三种暂不测试了,与此同样道理进行测试。

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/438494.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

r语言中1c0怎么表示什么,r语言表示或者用什么符号?

犯罪嫌疑人X1、对象名称中的句点(.)没有特殊意义。但美元符号($)却有着和其他语言中的句点类似的含义&#xff0c;即指定一个对象中的某些部分例如&#xff1a;(1)A$x是指数据框A中的变量x。(2)lm.x 指一个变量&#xff0c;lm$x则指对象lm的一个属性。2、 R不提供多行注释或块注…

第二十三节: EF性能篇(三)之基于开源组件 Z.EntityFrameWork.Plus.EF6解决EF性能问题

一. 开篇说明 EF的性能问题一直以来经常被人所吐槽&#xff0c;究其原因在于“复杂的操作在生成SQL阶段耗时长&#xff0c;且执行效率不高”&#xff0c;但并不是没有办法解决&#xff0c;从EF本身举几个简单的优化例子&#xff1a; ①&#xff1a;如果仅是查询数据&#xff0c…

c语言怎样计算栈的长度,数据结构与算法:栈 C语言实现

栈是仅在表尾进行插入、删除操作的线性表。即栈 S (a1, a2, a3, ………,an-1, an)&#xff0c;其中表尾称为栈顶 /top&#xff0c;表头称为栈底/base。由于只能在表尾进行操作&#xff0c;因此栈的运算规则就是“后进先出”(LIFO)和线性表类似&#xff0c;栈也有两种存储结构—…

WebApi系列(从.Net 到 .Net Core)【更新】

一. 简介 1. 什么是WebApi&#xff1f; WebApi是一个很广泛的概念&#xff0c;在这里我们特指.Net平台下的Asp.Net WebApi框架&#xff0c;它是针对各种客户端(浏览器、APP等)来构建Http服务的一个框架&#xff0c;它是一种RestFul风格的开发接口的技术&#xff0c;它比WebServ…

第一节:WebApi的纯原生态的RestFul风格接口和路由规则介绍

一. 原生态接口 1. 从默认路由开始分析 在WebApiConfig.cs类中的Register方法中&#xff0c;我们可以看到默认路由如下&#xff1a; 分析&#xff1a;请求地址在 controller 前面需要加上 api/&#xff0c;controller后面没有action&#xff0c;最后有一个参数{id}&#xff0c…

第二节:如何正确使用WebApi和使用过程中的一些坑

一. 基本调用规则 1. 前提 WebApi的默认路由规则为&#xff1a;routeTemplate: "api/{controller}/{id}", 下面为我们统一将它改为 routeTemplate: "api/{controller}/{action}/{id}",这样我们在调用的时候&#xff0c;还是通过拼接方法名来识别&#xff0…

第三节:总结.Net下后端的几种请求方式(WebClient、WebRequest、HttpClient)

一. 前言 前端调用有Form表单提交&#xff0c;ajax提交&#xff0c;ajax一般是用Jquery的简化写法&#xff0c;在这里不再过多介绍&#xff1b; 后端调用大约有这些&#xff1a;WebCient、WebRequest、Httpclient、WebapiClient&#xff0c;重点探讨Get和Post请求&#xff0c;P…

android 传感器ceshi,Android代码-传感器-测试手机支持那几种传感

Android代码----传感器-----测试手机支持那几种传感一个小小Demo检测手机支持那几种传感&#xff1a;具体代码如下&#xff1a;[Java代码]DemoSensorActivity.javapackage com.example.testsensor;import java.util.List;import android.app.Activity;import android.content.C…

第四节:跨域请求的解决方案和WebApi特有的处理方式

一. 简介 前言&#xff1a; 跨域问题发生在Javascript发起Ajax调用&#xff0c;其根本原因是因为浏览器对于这种请求&#xff0c;所给予的权限是较低的&#xff0c;通常只允许调用本域中的资源&#xff0c; 除非目标服务器明确地告知它允许跨域调用。假设我们页面或者应用已在 …

手机存储android文件怎么打开,安卓手机如何打开.jio文件?

01安卓手机无法打开.jio文件&#xff0c;需要在电脑上安装久其通用数据管理平台软件打开。进入软件首页&#xff0c;点击菜单栏“装入”按钮&#xff0c;弹出“数据装入向然后导”&#xff0c;单击文件夹图标&#xff0c;在弹出“打开”窗口中选择装入数据的路径&#xff0c;单…

第五节:WebApi的三大过滤器

一. 基本说明 1. 简介&#xff1a; WebApi下的过滤器和MVC下的过滤器有一些区别,首先我们要注意的是通常建WebApi项目时&#xff0c;会自动把MVC的程序集也引入进来&#xff0c;所以我们在使用WebApi下的过滤器的时候&#xff0c;要引入“ System.Web.Http”这个程序集&#x…

android动态贴纸实现原理,人脸动态贴纸sdk算法详解,人脸动态贴纸功能如何实现...

原标题&#xff1a;人脸动态贴纸sdk算法详解&#xff0c;人脸动态贴纸功能如何实现泛娱乐行业在互联网领域中发展趋势逐渐增强&#xff0c;而直播、短视频、视频社交等作为头部产品受到了众多用户的关注和喜爱。为了能够更好的满足用户体验&#xff0c;众多APP纷纷开始接入人脸…

第六节:WebApi的部署方式(自托管)

一. 简单说明 开篇就介绍过WebApi和MVC相比&#xff0c;其中优势之一就是WebApi可以不依赖于IIS部署&#xff0c;可以自托管&#xff0c;当然这里指的是 .Net FrameWork 下的 WebApi 和 MVC 相比较&#xff0c;在.Net Core下&#xff0c;当然就另行别论。 下面我们重点介绍的就…

第七节:WebApi与Unity整合进行依赖注入和AOP的实现

一. IOC和DI 1. 通过Nuget引入Unity程序集。 PS:【版本&#xff1a;5.8.6】 2. 新建DIFactory类&#xff0c;用来读取Unity的配置文件并创建Unity容器&#xff0c;需要注意的是DIFactory类需要声明成单例。 PS&#xff1a;这里采用静态构造函数(必须是无参的)的形式来实现单…

第八节:常见安全隐患和传统的基于Session和Token的安全校验

一. 常见的安全隐患 1. SQL注入 常见的案例&#xff1a; String query "SELECT * FROM T_User WHERE userID" Request["userID"] "; 这个时候&#xff0c;只需要在传递过来的userID后面加上个&#xff1a; or 11&#xff0c;即可以获取T_User表中…

android手机设置时间设置,如何设置电信定制手机日期与时间

使用电信定制手机如A765e、A600e、A560e等的时候&#xff0c;可能会发现手机的日期和时间没法自己设置。这主要是因为手机使用电信卡时&#xff0c;会自动与电信基站进行交互并自动调节日期与时间。所以不论使用电信定制的单模或双模手机(单模是指手机只有一个卡槽&#xff0c;…

第二节 CSS入门介绍

一.背景 这里将陆续介绍前端CSS中相关知识&#xff0c;先介绍CSS2.1&#xff0c;后续会介绍CSS3的相关属性&#xff0c;通过该系列的文章&#xff0c;希望能给准备转战前端的人员一些帮助&#xff0c;同时也帮助自己梳理知识&#xff0c;文章中如有错误&#xff0c;欢迎指出。 …

三星sec.android.soagent,3.0降级2.5教程

给小白看的。下载五件套&#xff0c;odia&#xff0c;驱动&#xff0c;地址&#xff1a;http://www.samsungmembers.cn/thread-1019962-110-150.html&#xff0c;或者自己论坛搜索&#xff0c;请下载G9810ZCU2BTJA&#xff0c;别下k3最后一个版本的会出问题&#xff0c;刷机后再…

第三节 入门属性

1.七个基本属性 颜色&#xff1a;color&#xff0c;背景颜色&#xff1a;background-color&#xff0c;字体大小&#xff1a;font-size&#xff0c;加粗&#xff1a;font-weight:bold&#xff0c;倾斜&#xff1a;font-style:italic&#xff0c; 文字居中&#xff1a;text-alig…

第四节 CSS继承性和层叠性

一. 继承性 1. 含义&#xff1a;从自己开始直到所包裹的最小的元素&#xff0c;都可以继承一些特有的属性。 2. 作用范围&#xff1a; a. color、text-开头的、line-开头的、font-开头的&#xff0c;均可以继承。 b. 文字样式的&#xff0c;都能继承&#xff1b;所有关于盒子的…