目录
- 背景
- 伪代码分析
- 并发测试
- 解决方案
背景
- 数据背景:我们有22个进件分表, 大概10亿的数据,为了方便查询进件, 我们原来封装了一个查询组件, 用来循环遍历这22张表的全部数据。
- 优化背景: 我们在分析变量计算系统的时候,发现很多sql只需要查询关联的一个数据, 或者只需要一个最热的数据。 这种情况下, 没必要把所有的表都遍历一遍。 因此主要有两点可优化点。
- 进件表查询必须调整成从热到冷检索。
- 新增一个stop参数, 可以控制查询到一个数据后就不再扫描后续的表。
伪代码分析
- 原始代码
from DBUtils.PooledDB import PooledDB
class MysqlOps(object):
def __init__(self):
self.table_list = list() # 进件分表信息
def _get_table_list(self):
"""获取进件分表信息"""
sql = "SELECT COUNT(id) FROM data_split_info; "
table_num = list(self.select(sql))[0][0]
self.table_list = [num for num in range(0, table_num)]
def mulselect(self, sql, apply_id, values=[], stop=False):
"""进件分表查询"""
self._get_table_list()
for num in self.table_list:
print("后续是遍历分表信息")
pass
- 新代码
from DBUtils.PooledDB import PooledDB
class MysqlOps(object):
def __init__(self):
self.table_list = list() # 进件分表信息
# 新逻辑:按照热表优先扫描。
def _get_table_list(self):
if len(self.table_list) == 0:
sql = "SELECT table_name FROM data_split_info order by id desc; "
rows = self.select(sql)
for row in rows:
self.table_list.append(row[0])
def mulselect(self, sql, apply_id, values=[], stop=False):
"""进件分表查询"""
self._get_table_list()
for num in self.table_list:
print("后续是遍历分表信息")
pass
- 新代码调整分析
- 新代码主要是将表顺序按照从热到冷的顺序输出了一下。
- 问题:主要的问题在于我用append方法给共享变量table_list由原来的赋值调整成了append追加值。
- 并发分析: 当两个请求同时判断self.table_list 长度为0的时候, 原来的逻辑后先后给这个变量赋值一个相同的值。因此虽然存在并发问题, 但self.table_list这个变量值不会发生任何变化。 但是调整成append后, 当两个请求同时判断self.table_list 长度为0的时候, 会导致self.table_list数据加倍。
并发测试
- 测试案例:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2023/8/15 11:09
# @Author : shanwen.ren
import time
from concurrent.futures import ThreadPoolExecutor
class Test(object):
def __init__(self):
self.table_list = list()
def _get_table_list(self):
if len(self.table_list) == 0:
for i in range(30):
time.sleep(0.01)
self.table_list.append(i)
def run(self):
self._get_table_list()
# 进程启动时初始化对象
this_test = Test()
def test():
"""进行测试"""
futures = []
try:
with ThreadPoolExecutor(max_workers=80) as executor:
for i in range(10):
futures.append(executor.submit(this_test.run))
for future in futures:
future.result()
except Exception as e:
print("error is {}".format(e))
# 输出最终的结果
print(this_test.table_list)
if __name__ == '__main__':
test()
- 输出结果
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 18, 19, 19, 19, 19, 19, 19, 19, 19, 20, 19, 20, 20, 20, 20, 20, 20, 20, 21, 20, 20, 21, 21, 21, 21, 21, 21, 21, 22, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 23, 22, 23, 23, 23, 23, 23, 23, 24, 23, 23, 23, 24, 24, 24, 24, 24, 25, 24, 24, 24, 24, 25, 25, 25, 25, 25, 26, 25, 25, 25, 25, 26, 26, 26, 26, 26, 27, 26, 26, 26, 27, 27, 26, 27, 27, 27, 28, 27, 27, 27, 28, 28, 28, 27, 28, 28, 29, 28, 29, 28, 28, 29, 28, 29, 29, 29, 29, 29, 29, 29]
解决方案
- 方法1、给共享变量赋值前加锁,同一时间只能一个线程对共享资源进行赋值操作。
- 方法2、将共享资源self.table_list放到类的初始化中进行获取赋值(保证进程启动后,请求进入前初始化)。
- 方法3、与之前一样, 用等号赋值的方式, 这样虽然有并发问题, 但是数据不会加倍。