分享

怎样在DB2中提高Insert性能的技巧(三)

 WindySky 2009-08-11
对于其他的应用程序,使用隔离级别UR。
 
  对于V8FixPak4,或许也可以通过DB2_EVALUNCOMMITTED注册表变量来减少锁冲突:如果将该变量设置为YES,那么在很多情况下,只能获得那些符合某个谓词的行上的锁,而并不是获得被检查的所有行上的锁。

  发出一个COMMIT命令以释放锁,因此如果更频繁地提交的话就足以减轻锁冲突的负担。

  注意

  在V7中,存在涉及insert和键锁的并发问题,但是在V8中,由于提供了type-2索引,这些问题实际上已经不见了。如果要迁移到V8中来,那么应该确保使用带CONVERT关键字的REORGINDEXES命令,以便将索引从type-1转换为type-2。

  在V7中,插入过程中可能使用W或NW锁,但是在V8中只有在使用了type-1索引或者隔离级别为RR的情况下才会出现这两种锁。因此,应尽可能避免这两种情况。

  一条insert所据有的锁(通常是一个X锁)通常不会受隔离级别的影响。例如,使用隔离级别UR不会阻止从插入的行上获得锁。然而,如果使用了INSERT...SELECT,则隔离级别将影响从SELECT获得的锁。

  6.日志记录

  缺省情况下,每条insert都会被记录下来,以用于恢复。日志记录首先被写到内存中的日志缓冲池,然后再写到日志文件,通常是在日志缓冲池已满或者发生了一次提交时写到日志文件的。对批量插入的日志记录的优化实际上就是最小化日志记录写的次数,以及使写的速度尽可能快。

  这里首先考虑的是日志缓冲池的大小,这由数据库配置参数LOGBUFSZ来控制。该参数缺省值为8页或32K,这与大多数批量插入所需的理想日志缓冲池大小相比要小些。举个例子,对于一个批量插入,假设对于每一行的日志内容有200字节,则在插入了160行之后,日志缓冲池就将被填满。如果要插入1000行,因为日志缓冲池将被填满几次,再加上提交,所以大概有6次日志写。如果将LOGBUFSZ的值增加到64页(256K)或者更大,缓冲池就不会被填满,这样的话对于该批量插入就只有一次日志写(在提交时)。通过使用更大的LOGBUFSZ可以获得大约13%的性能提升。较大日志缓冲池的不利之处是,紧急事故恢复所花的时间可能要稍微长一点。

  减少日志写的另一种可能性是对新行要插入到的那个表使用“ALTERTABLEACTIVATENOTLOGGEDINITIALLY”(NLI)。如果这样做了,那么在该工作单元内不会记录任何insert操作,但是这里存在两个与NLI有关的重要问题:

  如果有一条语句失败,那么这个表将被标记为不可访问的,并且需要被删除掉。这与其他恢复问题(请参阅SQLReference关于CreateTable的讨论)一起使得NLI在很多情况下不能成为可行的方法。

  在工作单元最后进行的提交,必须等到在此工作单元内涉及的所有脏页都被写到磁盘之后才能完成。这意味着这种提交要占用大量的时间。如果没有积极地进行页清除,那么在使用NLI的情况下,Insert加上提交所耗费的总时间要更长一些。将NLI与积极的页清除一起使用的时候,可以大大减少耗时。如果使用NLI,就要瞪大眼睛盯紧提交操作所耗费的时间。

  至于提高日志写的速度,有下面一些可能性:

  将日志与新行所要插入到的表分别放在不同的磁盘上。

  在操作系统层将日志分放到多个磁盘

  考虑为日志使用原始设备(rawdevice),但是要注意,这样管理起来要更困难些。

  避免使用RAID5,因为它不适合于写密集型(write-intensive)活动。

  7.提交

  提交迫使将日志记录写到磁盘上,以保证提交的插入肯定会存在于数据库中,并且释放新行上的锁。这些都是有价值的活动,但是因为Commit总是要牵涉到同步I/O(对于日志),而insert则不会,所以Commit的开销很容易高于insert的开销。因此,在进行批量插入时,每一行都提交一次的做法对于性能来说是很糟糕的,所以应确保不使用自动提交(对于CLI和CLP来说缺省情况正是如此)。建议大约每1000行提交一次:当每1000行而不是一两行提交一次时,性能可以提高大概10倍。不过,一次提交多于1000行只能节省少量的时间,但是一旦出现失败,恢复起来所花的时间要更多。

  对上述方法的一种修正:如果MINCOMMIT数据库配置参数的值大于1(缺省值),则DB2就不必对每次commit都进行一次同步I/O,而是等待,并试图与一组事件一起共享日志I/O。对于某些环境来讲,这样做是有好处,但是对于批量插入常常没有作用,甚至有负作用,因此,如果要执行的关键任务是批量插入,就应该让MINCOMMIT的值保持为1。

  可以选择性地进行改进的地方
 
  对于一次insert,有几种类型的处理将自动发生。如果您的主要目标只是减少插入时间,那么最简单的方法是避免所有这些处理的开销,但是如果从总体上考虑的话,这样做未必值得。让我们依次进行讨论。

  索引维护

  对于插入的每一行,必须添加一个条目到表上的每个索引中(包括任何主键索引)。这一过程主要有两方面的代价:

  遍历每个索引树,在树的每一层搜索一个页,以确定新条目必须存储在哪里(索引条目总是按键顺序存储的),这一过程所引起的CPU开销;

  将所有搜索到的页读入缓冲池,并最终将每个更新后的页写到磁盘上的I/O开销。

  更坏的场景是,在索引维护期间有大量的随机I/O。假设要插入10,000行,在索引的缓冲池中有5000页,并且要插入的各行的键值随机分布在整个键范围内。那么,有10,000个这么多的叶子页(可能还有些非叶子页)需要进入缓冲池,以便对它们进行搜索和/或更新,对于一个给定的叶子页,它预先已经在缓冲池中的概率只有10%。对于每次的insert,需要读磁盘的概率如此之高,使得这种场景往往性能很差。

  对于逐行插入,将新行添加到已有的索引中比起创建一个新索引来代价要高得多。如果是插入到一个空表,应该总是在进行了列插入之后创建索引。(注意,如果使用了load,则应该预先创建索引。)如果要插入到一个已经填充过的表,那么在列插入之前删除索引,并在列插入之后重新创建索引,这种方法可能是最快的,但是只有在要插入相当多的行--大概大于表的10-20%的时候,才能这么说。如果为索引表空间使用较大的缓冲池,并且尽可能地将不同insert排序,以便键值是排好序的,而不是随机的,就可以帮助加快索引维护。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多