perf: llmid缓存(启动预热+内存查),v1/chat/completions去3表JOIN

This commit is contained in:
yumoqing 2026-07-23 18:26:52 +08:00
parent 5e48622e2a
commit ef1e228f19
3 changed files with 64 additions and 17 deletions

View File

@ -23,6 +23,9 @@ from .utils import (
invalidate_uapi_cache,
get_llmusage_by_id,
read_ioinfo_content,
_warm_llmid_cache,
get_llmid_cached,
invalidate_llmid_cache,
)
from .llmclient import (
@ -122,10 +125,11 @@ order by lc.name, a.name"""
def _on_hot_reload(data=None):
"""Event handler for hot_reload — wraps invalidate_uapi_cache to accept dispatcher's data arg."""
"""Event handler for hot_reload — invalidate caches."""
from appPublic.log import debug
debug(f'[llmage] on_hot_reload called, invalidating uapi cache (data={data})')
debug(f'[llmage] on_hot_reload called, invalidating caches (data={data})')
invalidate_uapi_cache()
invalidate_llmid_cache()
def load_llmage():
@ -148,6 +152,10 @@ def load_llmage():
env.get_tpac_balance = get_tpac_balance
env.inference_generator = inference_generator
env.get_llms_by_catelog = get_llms_by_catelog
env.get_llmid_cached = get_llmid_cached
env.invalidate_llmid_cache = invalidate_llmid_cache
# 启动时预热 llmid 缓存
asyncio.ensure_future(_warm_llmid_cache(env))
env.get_llmcatelogs = get_llmcatelogs
env.checkCustomerBalance = checkCustomerBalance
env.get_llmproviders = get_llmproviders

View File

@ -63,6 +63,55 @@ def invalidate_uapi_cache(upappid=None, apiname=None):
_uapiio_cache.clear()
# =============================================================
# Process-level cache for llmid lookup (model+catelogid -> llmid)
# =============================================================
_llmid_cache = {} # key: "model_name:catelogid" -> llmid
async def _warm_llmid_cache(env):
"""启动时全量加载 model+catelogid -> llmid 映射"""
global _llmid_cache
try:
async with get_sor_context(env, 'llmage') as sor:
sql = """SELECT a.name, b.id as catelogid, m.llmid
FROM llm_api_map m
JOIN llm a ON a.id = m.llmid AND a.status = 'published'
JOIN llmcatelog b ON b.id = m.llmcatelogid"""
recs = await sor.sqlExe(sql, {})
for r in recs:
key = f"{r.name}:{r.catelogid}"
_llmid_cache[key] = r.llmid
debug(f'[llmage] llmid cache warmed: {len(_llmid_cache)} entries')
except Exception as e:
exception(f'[llmage] llmid cache warm failed: {e}')
_llmid_cache = {}
async def get_llmid_cached(env, model_name, catelogid):
"""从缓存获取 llmid未命中则查 DB 并缓存"""
global _llmid_cache
key = f"{model_name}:{catelogid}"
if key in _llmid_cache:
return _llmid_cache[key]
# 缓存未命中,查 DB兼容模型在缓存预热后新增的场景
async with get_sor_context(env, 'llmage') as sor:
sql = """SELECT m.llmid
FROM llm_api_map m
JOIN llm a ON a.id = m.llmid AND a.name = ${model}$ AND a.status = 'published'
JOIN llmcatelog b ON b.id = m.llmcatelogid AND (b.id = ${catelogid}$ OR b.name = ${catelogid}$)"""
recs = await sor.sqlExe(sql, {'model': model_name, 'catelogid': catelogid})
llmid = recs[0].llmid if recs else None
if llmid:
_llmid_cache[key] = llmid
return llmid
def invalidate_llmid_cache():
global _llmid_cache
_llmid_cache.clear()
async def update_llmusage(ns):
env = ServerEnv()
async with get_sor_context(env, 'llmage') as sor:

View File

@ -29,21 +29,11 @@ if not params_kw.prompt and not params_kw.messages:
d = return_error('Missing need data(prompt or messages)')
return json_response(d, status=400)
env = request._run_ns
async with get_sor_context(env, 'llmage') as sor:
sql = """select distinct a.* from llm a
join llm_api_map m on a.id = m.llmid
join llmcatelog b on m.llmcatelogid = b.id
where (b.id = ${catelogid}$ OR b.name = ${catelogid}$)
and a.name=${model}$
and a.status = 'published'"""
recs = await sor.sqlExe(sql, {
'catelogid': catelogid,
'model': params_kw.model or 'qwen3-max'
})
if len(recs) == 0:
debug(f'{params_kw.model=} not found')
return openai_400()
params_kw.llmid = recs[0].id
llmid = await env.get_llmid_cached(env, params_kw.model or 'qwen3-max', catelogid)
if not llmid:
debug(f'{params_kw.model=} not found for catelogid={catelogid}')
return openai_400()
params_kw.llmid = llmid
params_kw.llmcatelogid = catelogid
debug(f'{params_kw.llmid=}')