(INTERBASE之四) interbase技术探秘
原创kylixyqh
-------------------------------------------------------------------
Interbase6.0为了优化其服务器性能对其内部多个核心结构进行了增强,特别是多变体系结构、多线程服务结构、版本事务管理等。
1、垃圾收集线程(garbage collection thread)
在Interbase6.0以前版本中,每一个数据库请求都必须由垃圾收集协助完成,这显然降低了数据库的性能,因为当一个数据库请求在COMMIT提交阶段写入时会把所有数据页变脏。在Interbase6.0中,现在背景垃圾收集由一个中心线程控制执行,当某个数据页包含垃圾时Interbase6.0工作线程会立即通知垃圾线程,但通常情况下并不清除该数据页本身,COMMIT仅仅把由数据库请求实际更改的数据页写回去。总之,垃圾收集线程保管着所有脏数据页的列表,读取这些数据页,清除垃圾,最后将数据写回去。
2、事务清扫线程(sweep thread)
为确保数据库的一致性,Interbase6.0在使用一个事务清扫线程将会退的事物从数据库中清扫出去。
垃圾收集线程与事务清扫线程的优先权比数据库请求线程的优先权低,而且这两个线程是相互协作的,当事务清扫线程空闲时也会帮助垃圾清理线程清理数据垃圾。
3、文件格式变化(file format change)
Interbase6.0利用其独特的版本事务管理机制改善了数据记录存储的文件内部格式,使得一个数据页可以比前存储更多数据。
4、Versioning model transaction(版本模式事务处理)
要理解versioning模式的好处,搞明白interbase的versioning结构是如何同时很好地支持大型在线事务处理和数据决策,必须先清楚数据库的并发访问控制和事务处理。
[1]、锁。我们知道,最常用的处理多用户并发访问的方法是加锁。当一个用户锁住数据库中的某个对象时,其他用户就不能再访问该对象。加锁对并发访问的影响体现在锁的粒度上。比如,放在一个表上的锁限制对整个表的并发访问;放在数据页上的锁限制了对整个数据页的访问;放在行上的锁只限制对该行的并发访问。可见行锁粒度最小,并发访问最好,页锁粒度最大,表锁介于2者之间。锁有两种:悲观锁和乐观锁。悲观锁假定其他用户企图访问或者改变你正在访问、更改的对象的概率是很高的,因此在悲观锁的环境中,在你开始改变此对象之前就将该对象锁住,并且直到你提交了所作的更改之后才释放锁。悲观的缺陷是不论是页锁还是行锁,加锁的时间可能会很长,这样可能会长时间的限制其他用户的访问,也就是说悲观锁的并发访问性不好。与悲观锁相反,乐观锁则认为其他用户企图改变你正在更改的对象的概率是很小的,因此乐观锁直到你准备提交所作的更改时才将对象锁住,当你读取以及改变该对象时并不加锁。可见乐观锁加锁的时间要比悲观锁短,乐观锁可以用较大的锁粒度获得较好的并发访问性能。但是如果第二个用户恰好在第一个用户提交更改之前读取了该对象,那么当他完成了自己的更改进行提交时,数据库就会发现该对象已经变化了,这样,第二个用户不得不重新读取该对象并作出更改。这说明在乐观锁环境中,会增加并发用户读取对象的次数。
从数据库厂商的角度看,使用乐观的页锁是比较好的,尤其在影响很多行的批量操作中可以放比较少的锁,从而降低对资源的需求提高数据库的性能。再考虑聚集索引。在数据库中记录是按照聚集索引的物理顺序存放的。如果使用页锁,当两个用户同时访问更改位于同一数据页上的相邻两行时,其中一个用户必须等待另一个用户释放锁,这会明显地降低系统的性能。interbase和大多数关系数据库一样,采用的是乐观锁,而且读锁是共享的,写锁是排他的。可以在一个读锁上再放置读锁,但不能再放置写锁;你不能在写锁上再放置任何锁。锁是目前解决多用户并发访问的有效手段。
[2]、事务。在数据库中,事务处理是分三步进行的:首先你告诉数据库你开始一个事务,这样通知数据库把接下来的所有操作当作一个单元对待;接着,你对数据表作了更改;最后你要通知数据库是否要提交或者回退事务。当你提交事务时,所作的更改就变为永久性的;当你回退事务时,所有的更改都被撤销。事务之间的隔离程度使用isolation level表示的。其定义如下:
read uncommited:允许读取任何提交或未提交的记录,即不管记录是否提交都允许读取。
read commited:只允许读取已经提交的记录。
repeatable read:只能看到事务开始时刻的数据库的一个快照。事务开始后更该的所有记录不管提交未提交都无法看到。
Serializable:
大多数数据库默认的隔离级别是read commited.
事务处理对保证数据库的一致性是非常重要的。
事务和锁是密不可分的。考察数据库中记录的更改情况:
(1)、开始一个事务
(2)、读取行
(3)、存储行
(4)、作出更该
(5)重新读取该行看是否其他用户已经将其改变
(6)、锁住该行
(7)、写入更改
(8)、提交或者回退事务
(9)、释放锁
从上可以看出事务和锁的密切配合。
[3]、interbase 的versioning结构。在interbase6.0中,每一个事务都被分配一个顺序号即版本号,数据库维护着一个清册用来专门记录所有的事务的版本号、活动状况、提交、回退等情况。
版本模式下事务是这样的工作的:
当一个更该事务提交时,数据库会检查是否存在一个版本号小的事务仍处于活动状态,如果是,就创建该记录的一个新版本,每个新版本仅包含变化了的字段值(而不是复制原来的整条记录)以及创建它的事务的版本号。
当一个读事务开始时,这个读事务被分配下一个版本号。读事务请求读数据行时,数据库就会检查数据行最近版本的版本号是否大于读事务的版本号,而且还要检查在读事务开始时该版本对应的事务是否已经提交了。如果数据行的版本号大于读事务的版本号,或者当读事务开始时该数据行对应的事务仍然处于活动状态,interbase就会从事务清册中查找前一个版本,直到找到一个版本号比读事务版本号小而且在读事务开始时刻已经提交的事务版本,然后返回该版本对应的记录。考虑如下的例子,假定某行有如下几个版本:
Tran=100 (status=committed)
Tran=80 (status=active when read started)
Tran=60 (status=rolled back)
Tran=40 (status = committed when read started)
我们看一下一个顺序号为90的事务企图读取该行时的情景。
很显然,90无法看到版本100,因为版本100发生在他之后;90也不能读取版本80,尽管版本80顺序号比他小但版本80仍处于活动状态;90也不能读取版本60,因为版本60回退了;最后他只能读取版本40,因为版本40是在90开始时刻已经提交了而且顺序号也比他小。在这例子中,版本80也不能进行提交,因为版本100已经提交了,版本80只能回退。
下面我们通过几个例子说明加锁模式与版本模式的区别:
假定丈夫和妻子同时到不同的自动取款机取款。
在加锁模式下是这样进行的:
*丈夫读取帐户余额导致一个读锁
*妻子读取帐户余额导致一个读锁
*丈夫更该帐户余额放置写锁失败因为妻子已经放置了读锁
*妻子更该帐户余额放置写锁失败因为丈夫已经放置了读锁
这样就形成了一个死循环。数据库系统可能会发现该死循环回退全部事务。
在版本模式下是这样进行的:
*丈夫读取帐户余额
*妻子读取帐户余额
*丈夫更该帐户余额产生余额的新版本
*妻子更该余额被回退因为检测到余额有一个新版本存在
如果其中一个回退事务,就会出现另外的情形。
加锁模式下:
*丈夫读取帐户余额放置一个读锁
*丈夫提交余额更改放置一个写锁
*妻子读取帐户余额企图放置一个读锁但必须等待丈夫写锁释放
*丈夫回退事务并释放写锁
*妻子现在可以读取帐户余额并获取正确的值
版本模式下:
*丈夫更该帐户余额产生一个新的版本(未提交)
*妻子读取帐户余额,这并不影响丈夫未提交的事务
*丈夫回退事务并将版本标记为回退
可见版本模式下妻子不用读取两次帐户余额。
让我们继续。假设丈夫想获取一份关于他们所有帐户的总帐报告,而他的妻子此刻也正想更改某个帐户余额。让我们看一下在加锁模式下是如何保证丈夫报告的准确性的。
*丈夫读取所有帐户放置读锁
*妻子更该帐户放置写锁失败,他必须等到丈夫读事务完成
*丈夫获得了正确的总帐报告并释放读锁,妻子现在可以更该帐户了。
而在版本模式下:
*丈夫读取所有帐户获取总帐报告即开始读事务
*妻子更该帐户余额开始更该事务,然后提交更改并产生余额的新版本,这个版本对丈夫来说是不可见的。
*丈夫的读事务读取被妻子更该的余额时发现有多个版本存在,于是他就会沿着事务清册选择一个在他的读事务开始时刻已经提交而且版本号最大的记录,这样他就获得了正确的总帐报告,而一点都不影响妻子的更该事务。
通过对比可以看出,versioning模式的好处是在一个长的读事务过程中仍然允许更该事务的进行,即读事务和更该事务可以并发进行,而加锁模式是不允许的。也就是说版本模式可以很好的同时解决大型在线事务处理和数据决策支持。
最后让我们再看一下在数据库灾难恢复上加锁模式与版本模式有何不同。我们知道加锁模式的数据库是将事务存储在一个日志文件中的,当进行数据库的灾难恢复时必须从日志文件中读取信息到数据库并把在灾难发生时刻所有活动的事务全部予以回退以保证数据库的一致性,这个过程可能需要很长的时间;版本模式的数据库没有所谓的日志文件,数据库中记录的不同版本就已经提供了足够的信息进行数据库的灾难恢复,因此当数据库重新上线时它只是简单的扫描事务清册并把在再难发生时刻所有活动的事务全部标记为回退即可,根本不需要读取其它文件,所以这个过程十分迅速,这也是加锁模式数据库无法比拟的优点。