mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
perf: Redis 操作优化,移除号池 key 列表的 Usage 聚合查询
- 号池 key 列表移除 Usage 表聚合查询(total_tokens/total_cost_usd),消除慢 SQL - cooldown 计数从 SCAN 改为 SCARD (O(1)),通过 cooldown_idx SET 维护索引 - 读路径 Lua 脚本移除 ZREMRANGEBYSCORE,清理移至写路径减少开销 - affinity 清理从 KEYS 改为 SCAN 分批删除,invalidate_all_for_provider 改用 pipeline 批量 MGET+UNLINK - 缓存监控页添加清除按钮 loading 状态,Redis SCAN 并发限制为 4 - 提取 redis_utils 模块统一 SCAN+批量删除逻辑 - 调度热路径跳过 cooldown TTL 查询,account_state 预计算移出循环
This commit is contained in:
@@ -125,8 +125,6 @@ export interface PoolKeyDetail {
|
||||
cost_window_usage: number
|
||||
cost_limit: number | null
|
||||
request_count: number
|
||||
total_tokens: number
|
||||
total_cost_usd: number
|
||||
sticky_sessions: number
|
||||
lru_score: number | null
|
||||
created_at: string | null
|
||||
|
||||
@@ -44,6 +44,7 @@ const listLoading = ref(false)
|
||||
const tableKeyword = ref('')
|
||||
const matchedUserId = ref<string | null>(null)
|
||||
const clearingRowAffinityKey = ref<string | null>(null)
|
||||
const clearingAllAffinity = ref(false)
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(20)
|
||||
const currentTime = ref(Math.floor(Date.now() / 1000))
|
||||
@@ -203,6 +204,7 @@ async function clearAllCache() {
|
||||
})
|
||||
if (!secondConfirm) return
|
||||
|
||||
clearingAllAffinity.value = true
|
||||
try {
|
||||
await cacheApi.clearAllCache()
|
||||
showSuccess('已清除所有缓存')
|
||||
@@ -211,6 +213,8 @@ async function clearAllCache() {
|
||||
} catch (error) {
|
||||
showError('清除失败')
|
||||
log.error('清除所有缓存失败', error)
|
||||
} finally {
|
||||
clearingAllAffinity.value = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -571,6 +575,7 @@ onBeforeUnmount(() => {
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="h-8 w-8 text-muted-foreground/70 hover:text-destructive"
|
||||
:disabled="clearingAllAffinity"
|
||||
title="清除全部缓存"
|
||||
@click="clearAllCache"
|
||||
>
|
||||
|
||||
@@ -543,25 +543,13 @@
|
||||
>-</span>
|
||||
</TableCell>
|
||||
<TableCell class="py-3 px-2 align-middle">
|
||||
<div class="grid grid-rows-3 gap-0.5 w-[136px] mx-auto text-[10px] leading-4">
|
||||
<div class="w-[136px] mx-auto text-[10px] leading-4">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<span class="text-muted-foreground">请求</span>
|
||||
<span class="tabular-nums text-foreground/90">
|
||||
{{ formatStatInteger(key.request_count) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<span class="text-muted-foreground">Token</span>
|
||||
<span class="tabular-nums text-foreground/90">
|
||||
{{ formatTokenCount(key.total_tokens) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<span class="text-muted-foreground">费用</span>
|
||||
<span class="tabular-nums text-foreground/90">
|
||||
{{ formatStatUsd(key.total_cost_usd) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell class="py-3 text-center">
|
||||
@@ -924,19 +912,11 @@
|
||||
<div class="text-muted-foreground mb-0.5">
|
||||
统计
|
||||
</div>
|
||||
<div class="space-y-0.5 text-[10px]">
|
||||
<div class="text-[10px]">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<span class="text-muted-foreground">请求</span>
|
||||
<span class="tabular-nums">{{ formatStatInteger(key.request_count) }}</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<span class="text-muted-foreground">Token</span>
|
||||
<span class="tabular-nums">{{ formatTokenCount(key.total_tokens) }}</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<span class="text-muted-foreground">费用</span>
|
||||
<span class="tabular-nums">{{ formatStatUsd(key.total_cost_usd) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -2509,23 +2489,6 @@ function formatStatInteger(value: number | null | undefined): string {
|
||||
return Math.round(n).toLocaleString('en-US')
|
||||
}
|
||||
|
||||
function formatTokenCount(value: number | null | undefined): string {
|
||||
const n = Number(value ?? 0)
|
||||
if (!Number.isFinite(n) || n <= 0) return '0'
|
||||
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`
|
||||
if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`
|
||||
return String(Math.round(n))
|
||||
}
|
||||
|
||||
function formatStatUsd(value: number | null | undefined): string {
|
||||
const n = Number(value ?? 0)
|
||||
if (!Number.isFinite(n) || n <= 0) return '$0.00'
|
||||
if (n < 0.01) return `$${n.toFixed(4)}`
|
||||
if (n < 1) return `$${n.toFixed(3)}`
|
||||
if (n < 1000) return `$${n.toFixed(2)}`
|
||||
return `$${n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`
|
||||
}
|
||||
|
||||
function formatRelativeTime(isoStr: string): string {
|
||||
const date = new Date(isoStr)
|
||||
const pad = (n: number) => String(n).padStart(2, '0')
|
||||
|
||||
Reference in New Issue
Block a user