这里回复过:
http://topic.csdn.net/u/20071207/21/506850e3-a524-4b40-b919-9c9353d61a49.html

解决方案 »

  1.   

    啊,我的意思是想了解其实现原理啦,因为需要在中间层模拟出和数据库一致的行为。。
    另外其实这个和顶楼描述的情况还是有些不同的,在串行隔离级别之下,所有select出来的行都会被放上锁,但是,这时即使再加上放置表级锁,也无法阻止并发的更新语句把一些原本不符合条件的行(也就是没有被select出来的行)更新为符合条件啊?这样的话,被select出来的数据集和数据源就不一致了。。
      

  2.   

    关注一下LZ说的这种情况,完全看SELECT和UPDATE谁先被执行,并且还要看每个语句的执行时间谁长。1.   更新语句令条件搜索中的某些行不再符合条件 
    -----------------------------------------------
    a) 如果UPDATE先执行,将在需要修改的记录上放排斥锁,SELECT会等到UDPATE结束之后才能执行。上面情况不会发生。
    b) 如果SELECT先执行,在SELECT获得完整的结果集之前,有共享锁放在要作为返回集的纪录上,这个时候UPDATE能否执行应该看隔离级别的设置(?需要其他人确认一下)2.   更新语句令某些不符合条件的行变成符合条件 
    -----------------------------------------------
    和上面的分析类似。而且这种情况在多个用户环境下是完全有可能发生的。
    比如订票系统里面就很明显,当一个票务员搜索某个航班的时,第一次查询是没有座位。但与此同时,另外一个人退票了。那么当前面一个票务员再此查询时,就可能看到有个空位。我觉得这不是数据库放锁的问题,而是应用本身的特点,不能有死锁和很慢的响应是关键。产生这种情况的原因是这里面有两个完全不相关的事务。这和银行那种划账再取钱的一个完整事务不一样。
      

  3.   

    刚才做了下实验,mssql在串行隔离级别的并发更新和条件查询事务中确实是互斥的,也就是说,如果更新事务先执行,则条件查询事务被阻塞,反之亦然。。
      

  4.   

    在联机文档中看到了正解,设置 SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 就可以避免LZ所担心的情况。SERIALIZABLE
    Specifies that:*) Statements cannot read data that has been modified but not yet committed by other transactions.*) No other transactions can modify data that has been read by the current transaction until the current transaction completes.*) Other transactions cannot insert new rows with key values that would fall in the range of keys read by any statements in the current transaction until the current transaction completes.Range locks are placed in the range of key values that match the search conditions of each statement executed in a transaction. This blocks other transactions from updating or inserting any rows that would qualify for any of the statements executed by the current transaction. This means that if any of the statements in a transaction are executed a second time, they will read the same set of rows. The range locks are held until the transaction completes. This is the most restrictive of the isolation levels because it locks entire ranges of keys and holds the locks until the transaction completes. Because concurrency is lower, use this option only when necessary. This option has the same effect as setting HOLDLOCK on all tables in all SELECT statements in a transaction. 
      

  5.   


    对串行隔离级别,SET   TRANSACTION   ISOLATION   LEVEL   SERIALIZABLE 
    在一个事务中select 一个表中的记录,整个表都会被锁住,事务未提交前,其它事务的update或insert,delete语句是执行不了的.如果采用排它锁,可防止其它事务读记录.
      

  6.   

    谢谢,但是还有个疑问就是,用范围锁怎么可以防止以上情况的发生?想不通啊,例如一个条件查询事务选出了1-100号id的行,系统会锁定这个范围,这样虽然1-100不会被并发事务更改,但仍然有可能更改此范围之外的行导致那些更改了的行从不符合条件变成符合条件啊。
    -------------------------------------------------------
    呵呵,答案已经在7楼里面了,摘出来:
     This   blocks   other   transactions   from   updating   or   inserting   any   rows   that   would   qualify   for   any   of   the   statements   executed   by   the   current   transaction.   The   range   locks   are   held   until   the   transaction   completes.   如果其他的更新和插入操作涉及的记录符合当前事务的查询范围,则这些操作会被范围锁阻止,直到当前事务完成。如果LZ担心事务完成之后,又有新的更新和插入发生,并且又是之前事务的那个查询范围,那就是无法避免的。还是看4楼那个订票系统的例子。你只能保证一个事务存在时,数据的完整性得到保护。
      

  7.   

    嗯,如下这种情况,范围锁大概也没法保证数据一致吧:
    select id from article where userid = 1000;
    假如返回记录集:1,3,50,80
    如果按范围锁定,那么在查询事务结束前,id从1到80的记录不能被其它事务修改,但是如果此时有一个并发事务执行:
    update article set userid = 1000 where id = 81;
    如果这条语句被执行,那刚才返回的记录集就出现遗漏了,因为此时多了一条符合条件的记录(大概算是不可重复读?)。
    所以我觉得在条件查询时只有禁止插入、删除以及任何行的更新,才能完全保证一致性。。
      

  8.   

    嗯,如下这种情况,范围锁大概也没法保证数据一致吧: 
    select   id   from   article   where   userid   =   1000; 
    假如返回记录集:1,3,50,80 
    如果按范围锁定,那么在查询事务结束前,id从1到80的记录不能被其它事务修改,但是如果此时有一个并发事务执行: 
    ----------------------------------------------
    范围锁不是放在1到80之间,而是放在 userid = 1000 上面
    所有符合 userid = 1000 的记录都是锁定范围。
      

  9.   

    原来如此。。
    但是我觉得只有范围锁好像还是不能保证顶楼说的那两点啊(尤其是第2点)?还是上面的例子,id为81的行如果userid!=1000,它就不会被锁,也就有可能被并发更新为userid=1000了
    还是说锁的不是行而是条件?那如果一个条件查询很复杂,那效率岂不是很低了?难道还有列锁这东西?所有作为查询语句的查询条件的列都放上锁?
      

  10.   

    仔细读7楼的红色标注的内容,范围锁可以阻止其他SQL语句在相关查询范围内的新增更新(包括删除),甚至读取的操作。你的这个语句就是试图把UserID更新到锁定的范围之内(userid   =   1000)
    update   article   set   userid   =   1000   where   id   =   81; 
    这就会被范围锁阻止,不能被执行的。
      

  11.   

    对于特定主键的select、update、insert和delete,模拟行锁和表锁还是比较简单的,能比较容易实现与数据库系统类似的并发机制,现在碰到的问题就是条件查询事务与其它事务之间的并发控制,不好实现,所以才有了以上的疑问。。
      

  12.   

    串行隔离级事务环境中条件搜索语句(select)对相关结果集也不是加X锁 而更新语句一定会加某种范围的X锁1 如果select语句发生在前且未完成 update语句所涉及到的结果集(包括索引页和数据页)被等待加X锁 但这并不表明其他用户不能对该select涉及的结果集取数;另外,在改变事务隔离级的情况下更新语句可以发生 如read committed 2 如果update语句发生在前且未完成 若select查询涉及到update范围 它将被等待(block) 但可以(nolock) 去读脏数据 如果没涉及到update范围 正常输出结果集事务总有前后顺序 举例来讲我现在有三本书 借出去两本 我不知道人家还不还 别人问我现在可以借几本出去 那就是三本 待会人家又都还给我了 再问我就是五本