我所在的項目有個對賬系統,最初我是寫了腳本來校驗對賬結果的,對賬的2張表的資料量 大概是 100條 、 100w條;這次提測,手動造了幾百萬的資料,成了100萬條 、 100萬條 數量級,執行我的腳本,卻跑不出來結果,真的難住我了,做了些優化,分享下。
個人部落格:https://blog.csdn.net/zyooooxie
情景
這個系統是C-G的對賬【原始資料共6個表】,線上C的一個月資料量超過600w,因為對賬時間比較長,在更換技術方案後,提測給我;我在3個表每個都造了100-200w的資料,剩下3個表是爬蟲爬取回來的,是20w-200w的資料量。
其實,想要看對賬結果對不對,直接抽樣N條記錄,從對賬結果表 反推到 原始資料表,對比預期結果,就能校驗完。
但最初就是用的腳本來做,也就沒按上面去做,非靠我的腳本來搞。
非作死不可。
最早一版
最初2個表資料量分别是 100條 、100萬條,我寫的腳本跑的溜溜的,沒問題。
思路如下:
list_a 是C系統的資料list;list_b是G系統的資料list;
将2個list中 每個元素的4個字段進行比對,來把符合條件的元素扔到 對應的對賬結果list 【共6個結果,如下圖】
new_list = [a for a in list_a if a in list_b]
Log.info('Consistent-1000的有:{}'.format(new_list))
new_list1 = [[a, b] for a in list_a for b in list_b if a[0] == b[0] and a[2] != b[2] and a[1] == b[1]]
Log.info('Amounts Difference-1002的有:{}'.format(new_list1))
new_list2 = [[a, b] for a in list_a for b in list_b if a[0] == b[0] and a[3] != b[3] and a[2] == b[2] and a[1] == b[1]]
Log.info('Date difference-1003的有:{}'.format(new_list2))
new_list3 = [[a, b] for a in list_a for b in list_b if a[0] == b[0] and a[1] != b[1]]
Log.info('Status Difference-1001的有:{}'.format(new_list3))
new_list4 = list()
for a in list_a:
for b in list_b:
if a[0] != b[0]:
pass
else:
break
if b is list_b[-1]:
new_list4.append(a)
Log.info('Miss G Bill-1004的有:{}'.format(new_list4))
new_list5 = list()
for b in list_b:
for a in list_a:
if b[0] != a[0]:
pass
else:
break
if a is list_a[-1]:
new_list5.append(b)
Log.info('Miss C Bill-1005的有:{}'.format(new_list5))
第一版
這次優化是因為 最早一版用清單生成式看似代碼簡化,實際至少要執行6次循環嵌套;在資料量少的時候,無所謂;可當一個list是100w的長度,可能一次循環嵌套就要100w X 100w;是以優化為第一版,執行2次循環嵌套,拿出6個結果;
for a in list_a:
for b in list_b:
if a[0] != b[0]:
pass
else:
if a == b: # 4個字段全相同
consistent_1000.append([a, b])
elif a[0] == b[0] and a[3] != b[3] and a[2] == b[2] and a[1] == b[1]:
date_1003.append([a, b])
elif a[0] == b[0] and a[2] != b[2] and a[1] == b[1]:
amount_1002.append([a, b])
elif a[0] == b[0] and a[1] != b[1]:
status_1001.append([a, b])
break
if b is list_b[-1]:
g_miss_1004.append(a)
new_list5 = list()
for b in list_b:
for a in list_a:
if b[0] != a[0]:
pass
else:
break
if a is list_a[-1]:
new_list5.append(b)
但即便如此,想跑出來結果 還是沒戲啊。。。
【下圖是 其中1條用例 還沒執行完的日志】
第二版
其實這次優化,真的是很為難,已經用習慣了list;感覺沒啥思路。
和同僚溝通過 + 看到資料說:選擇合适的資料結構 就可以實作優化,
是以才想到用 字典;然後就被驚豔了【4個 100w X 100w的對帳 差不多3分鐘搞定】
怎麼用呢?
比如說:把list_a、list_b的元素 分别變為 某字典的key,在第二個字典查找這第一個字典的某個key,找到就說明 此key 2邊都有,即這個元素 2邊都有 =》 這個元素 屬于對賬結果中 4個字段值全相同的;
dict_a_new = dict.fromkeys(list_a, True)
dict_b_new = dict.fromkeys(list_b, True)
# 所有字段全相同的元素 list
consistent_1000 = [i for i in dict_a_new if i in dict_b_new]
那部分字段值相同呢?
我的思路:4個相同的(少) 和 3個相同的(多) 對比,某元素在3個相同 但不在4個相同,就說明此元素 第4個字段不同的;以此類推;
dict_a_new = dict.fromkeys(list_a, True)
dict_b_new = dict.fromkeys(list_b, True)
# 所有字段全相同的元素 list
consistent_1000 = [i for i in dict_a_new if i in dict_b_new]
# 全相同元素的前三個字段 list
new_consistent_1000_list = [(i[0], i[1], i[2]) for i in consistent_1000]
# 全相同元素的前三個字段 dict
new_consistent_1000_dict = dict.fromkeys(new_consistent_1000_list, True)
# 前三個字段 list
id_status_amont_list_a = [(i[0], i[1], i[2]) for i in list_a]
id_status_amont_list_b = [(i[0], i[1], i[2]) for i in list_b]
# 前三個字段 dict
id_status_amont_dict_a = dict.fromkeys(id_status_amont_list_a, True)
id_status_amont_dict_b = dict.fromkeys(id_status_amont_list_b, True)
# 前三個字段相同值的元素 list
id_status_amont = [i for i in id_status_amont_dict_a if i in id_status_amont_dict_b]
# 前三個字段相同值的元素 dict
id_status_amont_dict = dict.fromkeys(id_status_amont, True)
# 在前三個相同,但不在四個字段全相同 -> date不同
date_1003 = [i for i in id_status_amont_dict if i not in new_consistent_1000_dict]
最後看下 這樣的優化,實際執行的情況: 【下圖是 4條用例跑的 】
交流技術 歡迎 + QQ/微信 153132336 zy
個人部落格 https://blog.csdn.net/zyooooxie