TencentBlueKing / bk-nodeman

蓝鲸节点管理,可以对蓝鲸体系中的GSE Agent进行管理,包括状态查询、版本更新、配置管理、健康检查、进程管理等。
MIT License
51 stars 54 forks source link

[FEATURE] Big Key 缓存优化 #2093

Open CohleRustW opened 8 months ago

CohleRustW commented 8 months ago

背景

为什么需要

功能

你想要什么功能

功能实现

建议的方案

实现方案

功能自测

代码变更覆盖功能点需要自测并截图

功能点 1

描述代码变更涉及功能点及自测截图

功能点 2

描述代码变更涉及功能点及自测截图

...

CohleRustW commented 8 months ago

初版方案

缓存超过 5MB以上的都应该处理

  1. 通过判断 key大小决定是否分割
  2. 如果需要分割, 区分 REDIS BACKENDDB BACKEND 是否有区别处理,建议 DB 缓存使用 字符串分片
  3. 这里可能要考虑增加中间层去缓存分片信息
  4. 通过缓存的分片信息逐个 join 之前切分的 key
ghost commented 7 months ago

具体实现

采取字符串分片的方式存储:

[ ] 缓存内容通过转为utf-8字节表示判断大小并根据字节码拆分

[ ] 缓存内容通过sys.getsizeof计算判断大小并拆分

针对DB以3MB大小为标准对缓存项进行判断拆分

针对Redis以5MB大小为标准对缓存项进行判断拆分(Redis的存取比DB存储效率更高可以使用相对更大的分片存储来减少多次获取数据的动作)

分片大小可设置为可配置项

原有的key根据分片数量修改为key|{num}的形式并将其维护以 dict 的方式存储在缓存后端中

缓存数据为key|{num}: value_{num}形式存储到缓存后端中

获取时考虑使用多线程方式获取数据?

@ZhuoZhuoCrayon

ghost commented 7 months ago

修改apps/core/concurrent/cache.py 装饰器类FuncCacheDecorator

新增定义

DEFAULT_CACHE_PIECE_LENGTH_DB = 1024 * 1024 * 3
DEFAULT_CACHE_PIECE_LENGTH_REDIS = 1024 * 1024 * 5

新增分片相关函数

# 判断缓存对象大小
def check_cache_length(self, using: str, value: typing.Any) -> bool:
# 转utf-8计算缓存数据长度 根据缓存后端判断是否需要分片
    '''
    value转码
    if value > DEFAULT_CACHE_PIECE_LENGTH_DB:
        return True
    return False
    '''

# 分片函数
def split_cache(self, using: str, key: str, value: typing.Any) -> typing.Dict[str, str]:
# 根据缓存后端将缓存对象按对应的大小切分并返回分片字典
    '''
    片数 = value转码 / DEFAULT_CACHE_PIECE_LENGTH_DB
    parts = [value转码[i:i+1024*1024].decode('utf-8') for i in range(0, len(value转码), DEFAULT_CACHE_PIECE_LENGTH_DB)]
    return {f'key_{num}':parts[num] for num in range(片数)}
    '''

# 判断是否分片存储
def check_is_split(self, key: str, cache_result: typing.Any) -> bool
# 判断cache_result是不是分片的key组合成的列表
    '''
    cache_result判断是否为列表
    非列表返回False
    列表则判断是否形如key_{num}的形式,是返回True
    '''

# 组合分片信息函数
def join_splitted_cache(self, keys: List[str]) -> typing.Any
# 根据获取到的分片的key到缓存后端中获取对应的分片结果 按照keys顺序组合并返回
# 这里使用多线程的方式获取 等待任务完成后进行组合
    '''
    创建线程池
    并发任务 map()
    组合结果返回
    '''

修改相关函数

def get_from_cache(self, using: str, key: str) -> typing.Any
# 增加分片的判断分支
    '''
    是被分片的key走分片获取分支
    否则走原流程
    '''

def set_to_cache(self, using: str, key: str, value: typing.Any):
# 增加分片的判断分支
    '''
    是需要分片的key走分片分支存入缓存后端
    否则走原流程
    '''