转自:http://blog.csdn.net/wttxjp/archive/2009/06/13/4267200.aspx
最近工作中遇到一个问题,老大要求写个程序把300个大约每个有30000行记录的逗号分割CSV文件在30秒中之内加载到Oracle数据库的表中,并且不能用sqlldr。。。当时就郁闷了,心想这娃不是仇人派来整我的吧,咱别说别的就是只读300个文件,然后解析下字符串也差不多这么长时间啊,并且BT的是这些文件之间还要检查记录是否重复,判断标准是每行的前两个字段是否相同如果相同就是相同的。唉,再郁闷工作还是要继续的啊。一步步来吧。
首先多线程肯定是不能避免的了,最后用的是生产者和消费者的模型,一个读取线程做为生产者,一个加载线程做为消费者,生产者读取完成一个就将缓冲区发送给消费者,然后消费者加载,加载完成后,通知生产者继续读取下个文件,这样循环下去直至完成。将要加载的300个文件分为几个段,每个段对应一个读取线程和一个加载线程,多来几套生产者和消费者,估计性能会有较多的提高。
下面就对加载的方法进行了研究,oracle最快的加载方法就是sqlldr的direct path load模式了,幸好知道OCI支持这中挤在方法,那还等什么呢,三下五除二花了两天功夫搞了oci的加载函数,实验了下1000多万的记录,眨眼间就过去了!Oracle就是牛啊。我最担心的地方也没问题了,瞬间感觉心情愉快非常啊。可没想
最大的灾难居然在后面。
轮到写读取文件的函数了,轻车熟路,vector一大把,一测试晕倒读取一个文件居然要8秒左右。这自然无法接受了,优化吧,给vector先保留空间,不管用,因为这里用的vector全是二维的,唉,看来好用的东西不一定效率好啊,没办法全换成数组了,一测,好了点,不过还是要6秒左右。这是怎么回事,马上加了时间日志,发现时间的85%居然耗在了判断记录是否重复的那块了,每条记录都要到现有记录列表中遍历查看是否已经存在了,而且记录是乱序的,确实会比较费时间。原因知道了,怎么解决呢?对现有记录排序?可现有记录集合是变化的,也就意味着每次读取一跳记录会面临移动大量记录的可能,这估计也是有问题的,想来想去也没啥好方法。
下班回家,本来就要睡觉了,突然灵机一动,每条记录是由前两个字段来标识的,而前两个字段都是整形数字,范围也不是很大,为什么不先为范围内的每个数字都分配一个bool的空间,如果已经存在在列表中了对应的bool设置为true,迄今没有发现的设置为初始值false,这样每次读取一条记录直接以前两个字段做为数组下标到状态数组中索引不就知道重复没有重复嘛。这样也不用排序了,并且复杂度和记录数目没有关系都是一次内存访问即可了,不好的地方就是浪费了内存。反正现在效率优先,先试试再说吧。
爬起来,一顿猛敲,代码完工,一测试读取一个文件的时间全部为0了。300个文件只听硬盘咔咔响,三四秒钟就过去了,只是内存使用的比以前多的多了,不管了反正总算解决了,可以睡觉好觉了,哈哈。
今天在上网的时候发现一个排序算法叫做线性排序,一个俄国人发明的,主要思路就是把元素比较的过程转换为相对于原点的索引过程,比如要对1,2,3排序,那么1相对于原点0的索引是1,2相对于0的索引是2,3则是3了,当索引完成之后就完全了排序的过程,很显然时间复杂度是o(N)了,这很强啊,仔细想来自己上面想的那个歪路和这个似乎有种异曲同工之处,呵呵。