這兩天接個任務,幫人解決一個循環優化的問題。
和開發聯繫了一下,搞明白原來是這樣一個事。因爲數據庫比較緩慢,這些人決定把數據取到內存裏處理。兩張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()