mdbq 4.0.109__py3-none-any.whl → 4.0.111__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mdbq might be problematic. Click here for more details.
- mdbq/__version__.py +1 -1
- mdbq/redis/redis_cache.py +144 -14
- {mdbq-4.0.109.dist-info → mdbq-4.0.111.dist-info}/METADATA +1 -1
- {mdbq-4.0.109.dist-info → mdbq-4.0.111.dist-info}/RECORD +6 -6
- {mdbq-4.0.109.dist-info → mdbq-4.0.111.dist-info}/WHEEL +0 -0
- {mdbq-4.0.109.dist-info → mdbq-4.0.111.dist-info}/top_level.txt +0 -0
mdbq/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
VERSION = '4.0.
|
|
1
|
+
VERSION = '4.0.111'
|
mdbq/redis/redis_cache.py
CHANGED
|
@@ -16,6 +16,8 @@ import time
|
|
|
16
16
|
import threading
|
|
17
17
|
import socket
|
|
18
18
|
from datetime import datetime, date
|
|
19
|
+
from decimal import Decimal
|
|
20
|
+
from uuid import UUID
|
|
19
21
|
from typing import Optional, Dict, Any, List, Union
|
|
20
22
|
import redis
|
|
21
23
|
from mdbq.log import mylogger
|
|
@@ -32,16 +34,103 @@ logger = mylogger.MyLogger(
|
|
|
32
34
|
)
|
|
33
35
|
|
|
34
36
|
|
|
35
|
-
class
|
|
36
|
-
"""自定义JSON编码器,支持
|
|
37
|
+
class MySQLDataEncoder(json.JSONEncoder):
|
|
38
|
+
"""自定义JSON编码器,支持MySQL常见数据类型"""
|
|
37
39
|
def default(self, obj):
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
try:
|
|
41
|
+
if isinstance(obj, datetime):
|
|
42
|
+
return obj.isoformat()
|
|
43
|
+
elif isinstance(obj, date):
|
|
44
|
+
return obj.isoformat()
|
|
45
|
+
elif isinstance(obj, Decimal):
|
|
46
|
+
# Decimal转换为字符串保持精度
|
|
47
|
+
return str(obj)
|
|
48
|
+
elif isinstance(obj, UUID):
|
|
49
|
+
return str(obj)
|
|
50
|
+
elif isinstance(obj, bytes):
|
|
51
|
+
# 二进制数据转换为base64
|
|
52
|
+
import base64
|
|
53
|
+
return base64.b64encode(obj).decode('utf-8')
|
|
54
|
+
elif isinstance(obj, (set, frozenset)):
|
|
55
|
+
# 集合类型转换为列表
|
|
56
|
+
return list(obj)
|
|
57
|
+
elif hasattr(obj, 'isoformat'):
|
|
58
|
+
# 其他日期时间类型
|
|
59
|
+
return obj.isoformat()
|
|
60
|
+
elif hasattr(obj, '__float__'):
|
|
61
|
+
# 数值类型
|
|
62
|
+
return float(obj)
|
|
63
|
+
elif hasattr(obj, '__str__'):
|
|
64
|
+
# 其他有字符串表示的对象
|
|
65
|
+
return str(obj)
|
|
66
|
+
elif hasattr(obj, '__dict__'):
|
|
67
|
+
# 处理其他对象,转换为字典
|
|
68
|
+
return obj.__dict__
|
|
69
|
+
except Exception as e:
|
|
70
|
+
# 如果所有方法都失败,返回对象的字符串表示
|
|
71
|
+
return f"<{type(obj).__name__}: {str(obj)[:100]}>"
|
|
72
|
+
|
|
42
73
|
return super().default(obj)
|
|
43
74
|
|
|
44
75
|
|
|
76
|
+
class SmartTTLConfig:
|
|
77
|
+
"""智能TTL配置策略"""
|
|
78
|
+
|
|
79
|
+
# TTL策略映射表(秒)
|
|
80
|
+
TTL_STRATEGIES = {
|
|
81
|
+
# 数据库相关缓存
|
|
82
|
+
'sycm_database': 1800, # 数据库列表:30分钟
|
|
83
|
+
'sycm_tables': 1200, # 表列表:20分钟
|
|
84
|
+
'sycm_table_data': 300, # 表数据:5分钟
|
|
85
|
+
|
|
86
|
+
# 用户会话相关
|
|
87
|
+
'user_session': 3600, # 用户会话:1小时
|
|
88
|
+
'user_info': 1800, # 用户信息:30分钟
|
|
89
|
+
|
|
90
|
+
# 静态配置
|
|
91
|
+
'static_config': 86400, # 静态配置:24小时
|
|
92
|
+
'system_config': 43200, # 系统配置:12小时
|
|
93
|
+
|
|
94
|
+
# API相关
|
|
95
|
+
'api_response': 600, # API响应:10分钟
|
|
96
|
+
'api_cache': 900, # API缓存:15分钟
|
|
97
|
+
|
|
98
|
+
# 临时数据
|
|
99
|
+
'temp_data': 300, # 临时数据:5分钟
|
|
100
|
+
'short_cache': 60, # 短期缓存:1分钟
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@classmethod
|
|
104
|
+
def get_ttl(cls, namespace: str, default: int = 3600) -> int:
|
|
105
|
+
"""
|
|
106
|
+
根据命名空间获取对应的TTL值
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
namespace: 缓存命名空间
|
|
110
|
+
default: 默认TTL值(秒)
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
int: TTL值(秒)
|
|
114
|
+
"""
|
|
115
|
+
return cls.TTL_STRATEGIES.get(namespace, default)
|
|
116
|
+
|
|
117
|
+
@classmethod
|
|
118
|
+
def add_strategy(cls, namespace: str, ttl: int):
|
|
119
|
+
"""
|
|
120
|
+
添加新的TTL策略
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
namespace: 命名空间
|
|
124
|
+
ttl: TTL值(秒)
|
|
125
|
+
"""
|
|
126
|
+
cls.TTL_STRATEGIES[namespace] = ttl
|
|
127
|
+
|
|
128
|
+
@classmethod
|
|
129
|
+
def get_all_strategies(cls) -> Dict[str, int]:
|
|
130
|
+
"""获取所有TTL策略"""
|
|
131
|
+
return cls.TTL_STRATEGIES.copy()
|
|
132
|
+
|
|
133
|
+
|
|
45
134
|
class CacheConfig:
|
|
46
135
|
"""缓存系统配置类"""
|
|
47
136
|
|
|
@@ -67,6 +156,10 @@ class CacheConfig:
|
|
|
67
156
|
self.max_value_size = 1024 * 1024 # 1MB
|
|
68
157
|
self.batch_size = 100
|
|
69
158
|
|
|
159
|
+
# 热点键配置
|
|
160
|
+
self.max_hot_keys = 1000 # 最大热点键数量
|
|
161
|
+
self.hot_keys_cleanup_threshold = 800 # 热点键清理阈值
|
|
162
|
+
|
|
70
163
|
# MySQL数据库配置
|
|
71
164
|
self.db_name = db_name
|
|
72
165
|
self.table_name = table_name
|
|
@@ -223,6 +316,20 @@ class SmartCacheSystem:
|
|
|
223
316
|
"""记录热点键"""
|
|
224
317
|
cache_key = self._generate_cache_key(key, namespace)
|
|
225
318
|
with self.hot_keys_lock:
|
|
319
|
+
# 检查是否需要清理热点键(防止内存泄漏)
|
|
320
|
+
if len(self.hot_keys) >= self.config.hot_keys_cleanup_threshold:
|
|
321
|
+
# 保留访问次数最高的键,移除访问次数最少的键
|
|
322
|
+
sorted_keys = sorted(self.hot_keys.items(), key=lambda x: x[1], reverse=True)
|
|
323
|
+
# 保留前600个热点键,为新键留出空间
|
|
324
|
+
self.hot_keys = dict(sorted_keys[:600])
|
|
325
|
+
|
|
326
|
+
self.logger.info("热点键内存清理", {
|
|
327
|
+
'清理前数量': len(sorted_keys),
|
|
328
|
+
'清理后数量': len(self.hot_keys),
|
|
329
|
+
'清理阈值': self.config.hot_keys_cleanup_threshold
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
# 记录或更新热点键访问次数
|
|
226
333
|
self.hot_keys[cache_key] = self.hot_keys.get(cache_key, 0) + 1
|
|
227
334
|
|
|
228
335
|
def get(self, key: str, namespace: str = "", default=None) -> Any:
|
|
@@ -259,16 +366,19 @@ class SmartCacheSystem:
|
|
|
259
366
|
return default
|
|
260
367
|
|
|
261
368
|
def set(self, key: str, value: Any, ttl: int = None, namespace: str = "") -> bool:
|
|
262
|
-
"""设置缓存值"""
|
|
369
|
+
"""设置缓存值 - 支持智能TTL策略"""
|
|
263
370
|
start_time = time.time()
|
|
264
371
|
|
|
265
372
|
try:
|
|
266
373
|
cache_key = self._generate_cache_key(key, namespace)
|
|
267
|
-
|
|
374
|
+
|
|
375
|
+
# 使用智能TTL策略
|
|
376
|
+
if ttl is None:
|
|
377
|
+
ttl = SmartTTLConfig.get_ttl(namespace, self.config.default_ttl)
|
|
268
378
|
|
|
269
379
|
# 序列化值
|
|
270
380
|
if isinstance(value, (dict, list, tuple)):
|
|
271
|
-
serialized_value = json.dumps(value, ensure_ascii=False, sort_keys=True, cls=
|
|
381
|
+
serialized_value = json.dumps(value, ensure_ascii=False, sort_keys=True, cls=MySQLDataEncoder)
|
|
272
382
|
else:
|
|
273
383
|
serialized_value = str(value)
|
|
274
384
|
|
|
@@ -282,6 +392,15 @@ class SmartCacheSystem:
|
|
|
282
392
|
response_time = (time.time() - start_time) * 1000
|
|
283
393
|
|
|
284
394
|
self._record_operation('sets', response_time)
|
|
395
|
+
|
|
396
|
+
# 记录TTL策略使用情况
|
|
397
|
+
if namespace and ttl != self.config.default_ttl:
|
|
398
|
+
self.logger.debug("使用智能TTL策略", {
|
|
399
|
+
'namespace': namespace,
|
|
400
|
+
'ttl': ttl,
|
|
401
|
+
'key': key[:50] + "..." if len(key) > 50 else key
|
|
402
|
+
})
|
|
403
|
+
|
|
285
404
|
return bool(result)
|
|
286
405
|
|
|
287
406
|
except Exception as e:
|
|
@@ -481,7 +600,7 @@ class SmartCacheSystem:
|
|
|
481
600
|
stats_data['ops_per_second'],
|
|
482
601
|
stats_data['unique_keys_count'],
|
|
483
602
|
stats_data['uptime_seconds'],
|
|
484
|
-
json.dumps(stats_data['hot_keys'], ensure_ascii=False, cls=
|
|
603
|
+
json.dumps(stats_data['hot_keys'], ensure_ascii=False, cls=MySQLDataEncoder),
|
|
485
604
|
socket.gethostname(),
|
|
486
605
|
self.instance_name
|
|
487
606
|
))
|
|
@@ -501,12 +620,23 @@ class SmartCacheSystem:
|
|
|
501
620
|
self.logger.error(f"提交统计数据到MySQL失败: {e}")
|
|
502
621
|
|
|
503
622
|
def _cleanup_hot_keys(self):
|
|
504
|
-
"""
|
|
623
|
+
"""清理热点键统计 """
|
|
505
624
|
with self.hot_keys_lock:
|
|
506
|
-
|
|
507
|
-
|
|
625
|
+
current_count = len(self.hot_keys)
|
|
626
|
+
|
|
627
|
+
# 如果热点键数量超过最大限制,进行清理
|
|
628
|
+
if current_count > self.config.max_hot_keys:
|
|
629
|
+
# 保留访问次数最高的键
|
|
508
630
|
sorted_keys = sorted(self.hot_keys.items(), key=lambda x: x[1], reverse=True)
|
|
509
|
-
|
|
631
|
+
# 保留80%的热点键
|
|
632
|
+
keep_count = int(self.config.max_hot_keys * 0.8)
|
|
633
|
+
self.hot_keys = dict(sorted_keys[:keep_count])
|
|
634
|
+
|
|
635
|
+
self.logger.info("定期热点键清理", {
|
|
636
|
+
'清理前数量': current_count,
|
|
637
|
+
'清理后数量': len(self.hot_keys),
|
|
638
|
+
'保留比例': '80%'
|
|
639
|
+
})
|
|
510
640
|
|
|
511
641
|
def _cleanup_expired_mysql_data(self):
|
|
512
642
|
"""清理过期的MySQL统计数据"""
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
mdbq/__init__.py,sha256=Il5Q9ATdX8yXqVxtP_nYqUhExzxPC_qk_WXQ_4h0exg,16
|
|
2
|
-
mdbq/__version__.py,sha256=
|
|
2
|
+
mdbq/__version__.py,sha256=nKcO0o5cZBWmqPghn8vvDzV6hZj4cC8f1ESFBAKaOmA,19
|
|
3
3
|
mdbq/auth/__init__.py,sha256=pnPMAt63sh1B6kEvmutUuro46zVf2v2YDAG7q-jV_To,24
|
|
4
4
|
mdbq/auth/auth_backend.py,sha256=ZxKRXPXa2t9ngRZEXKM72MzcMvN-0OtiVDOhZRTrm3w,85948
|
|
5
5
|
mdbq/auth/rate_limiter.py,sha256=1m_Paxp8pDNpmyoFGRpFMVOJpbmeIvfVcfiQ2oH72qM,32850
|
|
@@ -26,7 +26,7 @@ mdbq/pbix/pbix_refresh.py,sha256=JUjKW3bNEyoMVfVfo77UhguvS5AWkixvVhDbw4_MHco,239
|
|
|
26
26
|
mdbq/pbix/refresh_all.py,sha256=OBT9EewSZ0aRS9vL_FflVn74d4l2G00wzHiikCC4TC0,5926
|
|
27
27
|
mdbq/redis/__init__.py,sha256=YtgBlVSMDphtpwYX248wGge1x-Ex_mMufz4-8W0XRmA,12
|
|
28
28
|
mdbq/redis/getredis.py,sha256=vpBuNc22uj9Vr-_Dh25_wpwWM1e-072EAAIBdB_IpL0,23494
|
|
29
|
-
mdbq/redis/redis_cache.py,sha256=
|
|
29
|
+
mdbq/redis/redis_cache.py,sha256=OxtbqHDRO2ko8VS6KkwtqJgPuOFsWvnzIyMIWasnmps,30719
|
|
30
30
|
mdbq/route/__init__.py,sha256=BT_dAY7V-U2o72bevq1B9Mq9QA7GodwtkxyLNdGaoE8,22
|
|
31
31
|
mdbq/route/analytics.py,sha256=dngj5hVwKddEUy59nSYbOoJ9C7OVrtCmCkvW6Uj9RYM,28097
|
|
32
32
|
mdbq/route/monitor.py,sha256=7gLyeq7TqnbhPwhUw0dg-hw9-0OWeKoMdMhcANSDGVs,42255
|
|
@@ -34,7 +34,7 @@ mdbq/route/routes.py,sha256=QVGfTvDgu0CpcKCvk1ra74H8uojgqTLUav1fnVAqLEA,29433
|
|
|
34
34
|
mdbq/selenium/__init__.py,sha256=AKzeEceqZyvqn2dEDoJSzDQnbuENkJSHAlbHAD0u0ZI,10
|
|
35
35
|
mdbq/selenium/get_driver.py,sha256=1NTlVUE6QsyjTrVVVqTO2LOnYf578ccFWlWnvIXGtic,20903
|
|
36
36
|
mdbq/spider/__init__.py,sha256=RBMFXGy_jd1HXZhngB2T2XTvJqki8P_Fr-pBcwijnew,18
|
|
37
|
-
mdbq-4.0.
|
|
38
|
-
mdbq-4.0.
|
|
39
|
-
mdbq-4.0.
|
|
40
|
-
mdbq-4.0.
|
|
37
|
+
mdbq-4.0.111.dist-info/METADATA,sha256=fyVLMYRQ4ArdtViITdjLvQtRlQLtT_lWomGcFhMYzAo,365
|
|
38
|
+
mdbq-4.0.111.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
|
39
|
+
mdbq-4.0.111.dist-info/top_level.txt,sha256=2FQ-uLnCSB-OwFiWntzmwosW3X2Xqsg0ewh1axsaylA,5
|
|
40
|
+
mdbq-4.0.111.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|