这两天接个任务,帮人解决一个循环优化的问题。

和开发联系了一下,搞明白原来是这样一个事。因为数据库比较缓慢,这些人决定把数据取到内存里处理。两张10w条左右的表写了个嵌套循环...


本来听到数据库数据加载内存这一个说法,立刻想到是不是可以加个memcached缓存。但是一看sql,各种复杂的逻辑和多表连接,还是算了。。。

我和他说,你这个思路完全是不正确的。数据库有问题应该explain sql看看到底慢在哪。。。加载内存这种事情应该是vfs层的缓存机制实现的么。如果你想把数据库都加载到内存里处理,不相当于做一个rdbms么,我哪有这能力。

然后我又问他,这些内存里的数据要不要和数据源进行同步?他说,数据取出来用一下,用完就丢。。

好吧。。。看来索引什么的也没意义了。。


最后,只能给了他一个O(m+n)的算法。做了一个demo给他,灰常简单的东西:

#!/usr/bin/env python
#coding:utf-8
#简单演示data1和data2的对key的内连接。更多种的表达式解析需要对模式进行重新设计。比如可以再写一个sql解析引擎
#想要实现的sql功能如下:
#select * from data1 where data1.value<5 and data2.value like 'a%' and data1.key=data2.key
import random
#---------------------------------------------
class Data1(object):
    '''
    第一张表的结构
    '''
    __slot__=('key','value')
    def __init__(self,key,value):
        self.key=key
        self.value=value
    def __str__(self):
        return str((self.key,self.value))
    def __repr__(self):
        return str((self.key,self,value))
class Data2(object):
    '''
    第二张表的结构
    '''
    __slot__=('key','value')
    def __init__(self,key,value):
        self.key=key
        self.value=value
    def __str__(self):
        return str((self.key,self.value))
    def __repr__(self):
        return str((self.key,self.value))
#-------------------------------------------
#where匹配的策略,每个条件变成一个工具类。设计的非常粗糙,只做算法演示用,可以用bool逻辑进行组合
class WhereStratege(object):
    @staticmethod
    def judge(data):
        pass
class Little_Then_5(WhereStratege):
    @staticmethod
    def judge(data):
        if data.value<5:
            return True
        else:
            return False
class Begin_With_A(WhereStratege):
    @staticmethod
    def judge(data):
        if data.value.startswith('a'):
            return True
        else:
            return False
       
#------------------------------------------------------
#下面是算法的基本逻辑实现。python里面自带hashtable作为其基础数据结构。.net平台基本库也有hashtable的实现。
#在System.Collectons命名空间下面。这些hashtable应该都是带自动扩容的功能。因为我不确定这个实现在大量数据
#下表现怎么样,不过应该是没什么问题的。如果发现在一定量级下hashtable性能出现明显下降。说明这个hashtable的
#碰撞非常严重。很有可能是load factor过高导致的。正常情况下hashtable里面读写数据的时间随着数量增长保持在一个
#稳定的水平。偶尔会出现2、3倍的时间可能是产生了碰撞。每隔一段时间会出现一个比较慢的插入,说明hashtable正在
#进行扩容。扩容的速度随着表的增长会越来越慢,但是每次扩容后距离下一次扩容的时间也越长。因此均摊后的hashtable
#的读写性能是O(1)的。
#这个demo里面的result表示结果数组。实际操作中的可能会取两张表的各种字段。随便用一个struct就可以了。因为两张表
#中能够匹配的数据的地址位置已经知道了。可以根据需要生成需要的结构。另外,接口设计也要进行改动。每张表一个匹配
#逻辑的数组。如果你想要同时对多个字段进行连接,如果是与关系,使用一个hashtable就可以了。如果是或关系,需要再
#建立一个hashtable。如果是多表连接,也是同样的道理。
#需要注意的是无论是hash表还是结果缓冲区,都会比较大。需要在堆中生成而不是在线程栈里面。python不太好表现这个。
#另外,hashtable只能用来实现等于的连接条件,无法实现偏序关系,但是我想应该不太会有偏序的,或是更奇怪的连接要求。
def connect_data(data_list1,data_list2,method1,method2):
    hashtable={}
    result=[]
    for data in data_list1:
        if method1.judge(data):
            hashtable[data.key]=data
    for data in data_list2:
        if data.key not in hashtable:
            continue
        elif method2.judge(data):
            result.append(hashtable[data.key])
    return result
#---------------------------------------------------------
#只测试了10*10的表,方便验证程序的正确性。由于算法本身很简单,很容易看出是O(m+n)的,demo里就不进行性能测试了。
#第一张表的key和value是随机的10以内的数。第二张表的key是5~14的数,value是abc字母随机的组合。
#本来想把两张表做成生成器,每次迭代的时候自动生成数据来节省内存空间。不过又想了一下,这不符合实际的环境。
def gen_string(length,chartable):
    '''
    在chartable字符表中随机选择字符生成长度为length的字符串
    '''
    return ''.join([random.choice(chartable) for i in range(length)])
def test():
    '''
    简单的测试用例
    '''
    keylist1=range(10)
    keylist2=range(5,15)
    valuelist1=range(10)
    valuelist2=[gen_string(3,['a','b','c']) for i in range(10)]
    random.shuffle(keylist1)
    random.shuffle(valuelist1)
    random.shuffle(keylist2)
    datalist1=map(Data1,keylist1,valuelist1)
    datalist2=map(Data2,keylist2,valuelist2)
    print '第一张表的数据:\n'
    for data in datalist1:
        print data
    print '\n第二张表的数据:\n'
    for data in datalist2:
        print data
    print '\n连接以后的数据:\n'
    r=connect_data(datalist1,datalist2,Little_Then_5,Begin_With_A)
    for data in r:
        print data
if __name__=='__main__':
    test()