谁能简单地解释一下为什么需要锁升级以及所谓的锁开销是什么?
当你更新一张表并锁定一行时,你需要以某种方式记录这个事实:那是一行,它已被更新并锁定。
当你更新百万行时,你需要做这百万次,因此有一些空间来保存百万锁。
SQL Server 在内存中保存一个锁列表,而 Oracle 在表空间中保存。
这可能是因为 Oracle 比较老(比我老),而 SQL Server 比 Oracle 还年轻。
从设计师的角度来看,将临时资源(如锁)保存在永久存储中并不是那么明显的解决方案。只提一件事:您可能需要磁盘写入才能执行SELECT FOR UPDATE。
Oracle 的核心功能是在 80 年代初期开发的,当时根本无法选择将内容保存在内存中。他们只是不得不以某种方式使用磁盘空间。
如果要使用磁盘空间,则必须在磁盘的某处放置一个锁。
如果不在行本身内,在哪里为行保留锁?
SQL Server 锁系统的开发人员在发明名为 Sybase 的 RDBMS 设计时,决定将临时的东西(即锁)存储在临时存储器(即 RAM)中。
但 Oracle 的设计总是平衡的:如果你的数据库中有 1,000,000 行,那么你就有 1,000,000 把锁的存储空间,如果你有 10 亿行,你可能会存储 10 亿把锁等等。
SQL Server 的设计在这个意义上是有缺陷的,因为您的 RAM 和 HDD 空间可能不平衡。您可能很容易拥有 16M 的 RAM 和几 TB 的磁盘空间。而你的记忆就是无法拥有所有的锁。
这就是为什么当锁计数达到一定限制时,SQL Server 决定升级锁:它不是为数据页中的 10 个单独的行(需要 10 条记录)保持锁,而是锁定整个数据页(需要 1 条记录)。
另一方面,Oracle 在更新行时,只是将锁直接写入数据页。
这就是为什么 Oracle 的锁是行级的。
Oracle 不会按照常识“管理”锁:例如,您无法获取 Oracle 中锁定页面的列表。
当一个事务需要更新一行时,它只是去该行看看它是否被锁定。
如果是,它会查看哪个事务持有锁(此信息包含在数据页的锁描述符中)并将自身添加到该事务的通知队列中:当锁定事务终止时,原始事务会收到通知并锁定数据。
从并发的角度来看,锁升级完全是穷人的解决方案:它不会增加并发性。比如说,你可以锁定你甚至没有碰过的行。
从性能的角度来看,在内存中做事当然比在磁盘上做事要快。
但是由于Oracle缓存了数据块,并且上述实际操作无论如何都是在内存中执行的,所以性能是相同或接近的。