为了处理并发问题,锁定——任何形式的锁定,无论是行锁定、表锁定还是数据库锁定,都是一个好的解决方案吗?
如果没有,如何处理并发问题?
如果你相信甲骨文,不,根本不相信。那是因为甲骨文竭尽全力避免它。
问题是读者可以阻止写入者,而写入者会阻止读取者,而写入者必须等到所有读取者都完成一行后才能写入。这会延迟写入过程及其调用者。排他锁(用于写入)被保留到事务结束,以防事务必须回滚 - 这会阻止其他事务看到新值,直到事务提交。
实际上,如果没有太多争用,锁定通常是可以的,就像任何并发编程一样。如果对行/页/表的争用过多(没有多少数据库服务器会进行整个 DB 锁定),则会导致事务依次执行而不是并发执行。
Oracle 使用行版本控制,而不是锁定一行来写入它,而是创建该行的新版本。需要重复阅读的读者会记住他们阅读的行的版本。但是,如果正在记住其读取的读取器尝试更新自该事务读取以来已由另一个写入器更新的行,则会发生错误;这是为了阻止丢失的更新。为确保您可以更新一行,您必须说 SELECT 是 FOR UPDATE;如果你这样做,它需要一个锁——一次只有一个事务可以保存一行 FOR UPDATE,并且一个冲突的事务必须等待。
SQL Server 2005 及更高版本支持快照隔离,这是它们对行版本控制的名称。同样,如果您需要更新刚刚读取的某些数据,您应该请求更新锁 - 在 SQL Server 中,使用 WITH (UPDLOCK)。
锁定的另一个问题是死锁的可能性。这只是两个事务各自持有另一个需要的资源的锁,或者通常一个事务循环持有彼此需要进行的锁。数据库服务器通常会检测到此死锁并终止其中一个事务,将其回滚——然后您需要重试该操作。任何有多个并发事务修改相同行的情况都可能发生死锁。如果以不同的顺序触摸行,则会发生死锁;很难强制执行数据库服务器将使用的顺序(通常您希望优化器选择最快的顺序,这在不同的查询中不一定是一致的)。
一般来说,我会建议与线程相同 - 使用锁,直到您可以证明它们导致了可伸缩性问题,然后研究如何使最关键的部分无锁。
你必须首先定义你的目标。在并发请求的情况下,您想赢得最后一个用户还是第一个用户。数据库锁定当然是一个坏方法。尝试尽可能晚地锁定表/行并尽快释放锁定。
我认为不是,因为它一直“向下”,使您的应用程序更加复杂。大多数语言都有出色的并发处理技术,即使这样,最好的方法是编写可以“本机”处理并发的代码。
有关 Java 并发的更多信息:http: //java.sun.com/docs/books/tutorial/essential/concurrency/index.html
数据库锁定 - 与表或行锁定相反 - 是处理并发性的不好方法;它排除了并发性。
除了必须自己编写某种 DMBS 锁定代码之外,还有许多合理的替代方案。请记住,某种锁定确实总是在发生(原子操作等),但关键是如果您不需要,您不想去那里。如果没有必要,你不会想和哲学家一起吃饭。事务是绕过锁定的一种方式,但主要用于提交。使用标记/指示记录脏的字段(脏位模式)是另一种方式,只需确保您以原子访问方式执行此操作。正如之前的帖子所引用的那样,该语言通常有一个合适的解决方案,但是该语言通常只支持应用程序、进程到进程、级别并发,有时并发必须是在数据库中。我不想假设您有一个丰富的应用程序层,但如果您这样做了,那么有许多抽象层可以为您处理这个问题。Oracle 的 TopLink 是免费的,并且是 Hibernate 更强大的兄弟,两者都通过抽象、脏位、缓存和惰性锁定帮助您管理数据库并发挑战。你真的不想自己实现这些,除非你正在为学校或个人项目编码。明白问题,但站在巨人的肩膀上。
您是要在应用程序中处理并发,还是要解决您在数据库中遇到的并发问题。如果是前者,我觉得这不是一个好方法。如果是后者,这可能是您唯一的答案,而无需重新设计您的模式。