悲观锁丶乐观锁丶排它锁丶行锁丶处理高并发性问题

乐观锁的使用:

  1. 使用自增长的整数表示数据版本号。更新时检查版本号是否一致,比如数据库中数据版本为6,更新提交时version=6+1,使用该version值(=7)与数据库version+1(=7)作比较,如果相等,则可以更新,如果不等则有可能其他程序已更新该记录,所以返回错误。
  2. version方式:一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。update table set x=x+1, version=version+1 where id=#{id} and version=#{version};
  3. 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
  4. 在乐观锁模式下,没有锁操作会得到执行,事务只是验证是否有其它事务修改数据,如果有则进行事务回滚,否则就提交。

两种解决方案

  • 使用数据版本(Version)记录机制实现,这是乐观锁最常用的一种实现方式。何谓数据版本?即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。
  • 乐观锁定的第二种实现方式和第一种差不多,同样是在需要乐观锁控制的table中增加一个字段,名称无所谓,字段类型使用时间戳(timestamp), 和上面的version类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。

悲观锁的使用:

  1. 需要使用数据库的锁机制,比如SQL SERVER 的TABLOCKX(排它表锁) 此选项被选中时,SQL Server 将在整个表上置排它锁直至该命令或事务结束。这将防止其他进程读取或修改表中的数据。

    在实际生产环境里边,如果并发量不大且不允许脏读,可以使用悲观锁解决并发问题;但如果系统的并发非常大的话,悲观锁定会带来非常大的性能问题,所以我们就要选择乐观锁定的方法. 
    悲观锁会造成访问数据库时间较长,并发性不好,特别是长事务。 
    乐观锁在现实中使用得较多,厂商较多采用。

  2. 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。 通过 jdbc 实现时 sql 语句只要在整个语句之后加 for update 即可。例如: select …for update
  3. 使用悲观锁可以获得什么样的锁?

      使用悲观锁可以获得四种类型的锁:共享(Shared)、独占(Exclusive),更新(Update)和意图(Intent),前两个是真实的锁,后面两个是锁和标记的混合。

 解决方案:同时执行以下SQL语句,第二个查询语句则等待10秒后出结果。提交之后再解锁

begin tran
select * from Sys_Items with (TABLOCKX) where Id=2--表锁
Update Sys_Items set ItemName='Changed4' where Id=2
WAITFOR DELAY '000:00:10'
commit tran
select * from Sys_Items where Id=2

乐观锁( Optimistic Locking )相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。

排它锁的使用:

  1. 排它锁又称为写锁((eXclusive lock,简记为X锁)),若事物T对数据对象A加上X锁,则只允许T读取和修改A,其它任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。它防止任何其它事务获取资源上的锁,直到在事务的末尾将资源上的原始锁释放为止。
  2. 在第一个连接中执行以下语句
    begin tran
    update table1
    set A='aa'
    where B='b2'
    waitfor delay '00:00:30' --等待30秒
    commit tran
    在第二个连接中执行以下语句
    begin tran
    select * from table1
    where B='b2'
    commit tran

    若同时执行上述两个语句,则select查询必须等待update执行完毕才能执行即要等待30秒

行锁的使用:

A开始执行:

1 SET TRANSACTION ISOLATION LEVEL REPEATABLE READ 
2 begin tran 
3 select * from tablename with (rowlock,UpdLock) where id=3 
4 waitfor delay '00:00:05' 
5 commit tran 

B在同时执行:

update tablename set colname='10' where id=3 --则要等待5秒 
update tablename set colname='10' where id <>3 --可立即执行 

ROWLOCK语法可以使用在SELECT, UPDATE和DELETE语句中

使用行锁注意点:

1.如果你错误地使用在过多行上,数据库并不会聪明到自动将行级锁升级到页面锁,服务器也会因为行级锁的开销而消耗大量的内存和CPU,直至无法响应。

2.select 语句中,RowLock在不使用组合的情况下是没有意义的,With(RowLock,UpdLock) 这样的组合才成立,查询出来的数据使用RowLock来锁定,当数据被Update的时候,锁将被释放

ADO.NET的使用:

事务隔离级别:https://docs.microsoft.com/zh-cn/sql/t-sql/language-elements/transaction-isolation-levels?view=sql-server-2017

隔离级别的详细信息:https://docs.microsoft.com/zh-cn/sql/t-sql/statements/set-transaction-isolation-level-transact-sql?view=sql-server-2017 

BeginTransaction:详情了解

案例:

 1  /// BeginTransaction(IsolationLevel)定义事务的隔离级别
 2             /// ReadCommitted:默认项,在正在读取数据时保持共享锁,以避免脏读,但是在事务结束之前可以更改数据,从而导致不可重复的读取或幻像数据。
 3             /// ReadUncommitted:可以进行脏读,意思是说,不发布共享锁,也不接受独占锁。
 4             /// RepeatableRead:在查询中使用的所有数据上放置锁,以防止其他用户更新这些数据。 防止不可重复的读取,但是仍可以有幻像行。
 5             /// Serializable:在 System.Data.DataSet 上放置范围锁,以防止在事务完成之前由其他用户更新行或向数据集中插入行。
 6             /// Snapshot:通过在一个应用程序正在修改数据时存储另一个应用程序可以读取的相同数据版本来减少阻止。 表示您无法从一个事务中看到在其他事务中进行的更改,即便重新查询也是如此。
 7             ///
 8 private static void ExecuteSqlTransaction(string connectionString)
 9 {
10     using (SqlConnection connection = new SqlConnection(connectionString))
11     {
12         connection.Open();
13 
14         SqlCommand command = connection.CreateCommand();
15         SqlTransaction transaction;
16 
17         // Start a local transaction.
18         transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);
19 
20         // Must assign both transaction object and connection
21         // to Command object for a pending local transaction
22         command.Connection = connection;
23         command.Transaction = transaction;
24 
25         try
26         {
27             command.CommandText =
28                 "Insert into Region (RegionID, RegionDescription) VALUES (100, 'Description')";
29             command.ExecuteNonQuery();
30             command.CommandText =
31                 "Insert into Region (RegionID, RegionDescription) VALUES (101, 'Description')";
32             command.ExecuteNonQuery();
33             transaction.Commit();
34             Console.WriteLine("Both records are written to database.");
35         }
36         catch (Exception e)
37         {
38             try
39             {
40                 transaction.Rollback();
41             }
42             catch (SqlException ex)
43             {
44                 if (transaction.Connection != null)
45                 {
46                     Console.WriteLine("An exception of type " + ex.GetType() +
47                         " was encountered while attempting to roll back the transaction.");
48                 }
49             }
50 
51             Console.WriteLine("An exception of type " + e.GetType() +
52                 " was encountered while inserting the data.");
53             Console.WriteLine("Neither record was written to database.");
54         }
55     }
56 }
View Code

lock的使用

1 private static object obj = new object();//在页面中定义全局的锁
2
3 lock (obj)
4 {
5 
6 }

文章参考地址:http://tech.it168.com/a2010/1019/1115/000001115345_all.shtml

       https://www.cnblogs.com/sthinker/p/5922967.html

begin transelect * from Sys_Items with (TABLOCKX) where Id=2--表锁Update Sys_Items set ItemName='Changed4' where Id=2WAITFOR DELAY '000:00:10'commit tran

作者:chenze
出处:https://www.cnblogs.com/chenze-Index/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
如果文中有什么错误,欢迎指出。以免更多的人被误导。
原文地址:https://www.cnblogs.com/chenze-Index/p/9668880.html