Skip to content

Commit 86d580e

Browse files
authored
Optimize redis batch get and delete operations (#899)
* Optimize redis batch get and delete operations * Fix typo
1 parent cdbe37d commit 86d580e

File tree

3 files changed

+28
-16
lines changed

3 files changed

+28
-16
lines changed

backend/app/admin/api/v1/monitor/online.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
async def get_sessions(
1919
username: Annotated[str | None, Query(description='用户名')] = None,
2020
) -> ResponseSchemaModel[list[GetTokenDetail]]:
21-
token_keys = await redis_client.keys(f'{settings.TOKEN_REDIS_PREFIX}:*')
21+
token_keys = await redis_client.get_prefix(f'{settings.TOKEN_REDIS_PREFIX}:*')
2222
online_clients = await redis_client.smembers(settings.TOKEN_ONLINE_REDIS_PREFIX)
2323
data: list[GetTokenDetail] = []
2424

backend/app/admin/service/auth_service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ async def refresh_token(*, db: AsyncSession, request: Request) -> GetNewToken:
208208
raise errors.NotFoundError(msg='用户不存在')
209209
if not user.status:
210210
raise errors.AuthorizationError(msg='用户已被锁定, 请联系统管理员')
211-
if not user.is_multi_login and await redis_client.keys(match=f'{settings.TOKEN_REDIS_PREFIX}:{user.id}:*'):
211+
if not user.is_multi_login and await redis_client.get_prefix(f'{settings.TOKEN_REDIS_PREFIX}:{user.id}:*'):
212212
raise errors.ForbiddenError(msg='此用户已在异地登录,请重新登录并及时修改密码')
213213
new_token = await create_new_token(
214214
refresh_token,

backend/database/redis.py

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,26 +38,38 @@ async def open(self) -> None:
3838
log.error('❌ 数据库 redis 连接异常 {}', e)
3939
sys.exit()
4040

41-
async def delete_prefix(self, prefix: str, exclude: str | list[str] | None = None) -> None:
41+
async def delete_prefix(self, prefix: str, exclude: str | list[str] | None = None, batch_size: int = 1000) -> None:
4242
"""
4343
删除指定前缀的所有 key
4444
45-
:param prefix: 前缀
46-
:param exclude: 排除的 key
45+
:param prefix: 要删除的键前缀
46+
:param exclude: 要排除的键或键列表
47+
:param batch_size: 批量删除的大小,避免一次性删除过多键导致 Redis 阻塞
4748
:return:
4849
"""
49-
keys = []
50+
exclude_set = set(exclude) if isinstance(exclude, list) else {exclude} if isinstance(exclude, str) else set()
51+
batch_keys = []
52+
5053
async for key in self.scan_iter(match=f'{prefix}*'):
51-
if isinstance(exclude, str):
52-
if key != exclude:
53-
keys.append(key)
54-
elif isinstance(exclude, list):
55-
if key not in exclude:
56-
keys.append(key)
57-
else:
58-
keys.append(key)
59-
if keys:
60-
await self.delete(*keys)
54+
if key not in exclude_set:
55+
batch_keys.append(key)
56+
57+
if len(batch_keys) >= batch_size:
58+
await self.delete(*batch_keys)
59+
batch_keys.clear()
60+
61+
if batch_keys:
62+
await self.delete(*batch_keys)
63+
64+
async def get_prefix(self, prefix: str, count: int = 100) -> list[str]:
65+
"""
66+
获取指定前缀的所有 key
67+
68+
:param prefix: 要搜索的键前缀
69+
:param count: 每次扫描批次的数量,值越大扫描速度越快,但会占用更多服务器资源
70+
:return:
71+
"""
72+
return [key async for key in self.scan_iter(match=f'{prefix}*', count=count)]
6173

6274

6375
# 创建 redis 客户端单例

0 commit comments

Comments
 (0)