Archery查询功能与数据字典管理实战
Archery作为企业级SQL审核查询平台,提供了强大的SQL查询编辑器、多层次的安全限制机制、数据脱敏功能以及数据字典自动生成与导出能力。本文深入探讨了其查询权限校验体系、SQL语句安全过滤、查询执行流程、数据隐私保护机制,以及如何利用数据字典功能进行数据库结构管理和文档化,为企业数据库管理提供全面的安全保障和效率提升。
SQL查询编辑器功能与安全限制
Archery作为企业级SQL审核查询平台,其SQL查询编辑器不仅提供了强大的查询功能,更内置了多层次的安全限制机制,确保数据库查询操作的安全性和合规性。本文将深入探讨Archery SQL查询编辑器的核心功能和安全限制机制。
查询权限校验体系
Archery实现了精细化的查询权限控制体系,通过query_priv_check函数对用户查询请求进行全面校验:
def query_priv_check(user, instance, db_name, sql_content, limit_num):
"""
查询权限校验核心逻辑
:param user: 用户对象
:param instance: 数据库实例
:param db_name: 数据库名称
:param sql_content: SQL语句内容
:param limit_num: 查询限制行数
:return: 权限校验结果
"""
result = {"status": 0, "msg": "ok", "data": {"priv_check": True, "limit_num": 0}}
# 管理员权限检查
if user.has_perm("sql.query_all_instances"):
priv_limit = int(SysConfig().get("admin_query_limit", 5000))
result["data"]["limit_num"] = min(priv_limit, limit_num) if limit_num else priv_limit
return result
# MySQL表级权限校验
if instance.db_type == "mysql":
try:
# explain和show create语句跳过权限校验
if re.match(r"^explain|^show\s+create", sql_content, re.I):
return result
# 表引用解析和权限验证
table_ref = _table_ref(sql_content, instance, db_name)
for table in table_ref:
if not _db_priv(user, instance, table["schema"]) and not _tb_priv(
user, instance, table["schema"], table["name"]
):
result["status"] = 2
result["msg"] = f"你无{table['schema']}.{table['name']}表的查询权限!"
return result
# 权限限制行数计算
for table in table_ref:
priv_limit = _priv_limit(user, instance, db_name=table["schema"], tb_name=table["name"])
limit_num = min(priv_limit, limit_num) if limit_num else priv_limit
result["data"]["limit_num"] = limit_num
except Exception as msg:
logger.error(f"权限校验异常: {traceback.format_exc()}")
result["status"] = 1
result["msg"] = f"权限校验异常: {msg}"
# 其他数据库类型权限校验
else:
dbs = [db_name] if instance.db_type in ["redis", "mssql", "pgsql"] else [
i["schema"].strip("`") for i in extract_tables(sql_content) if i["schema"] is not None
]
dbs = list(set(dbs))
dbs.sort()
for db_name in dbs:
if not _db_priv(user, instance, db_name):
result["status"] = 2
result["msg"] = f"你无{db_name}数据库的查询权限!"
return result
for db_name in dbs:
priv_limit = _priv_limit(user, instance, db_name=db_name)
limit_num = min(priv_limit, limit_num) if limit_num else priv_limit
result["data"]["limit_num"] = limit_num
return result
SQL语句安全过滤机制
Archery通过filter_sql方法对SQL语句进行安全过滤,防止恶意查询和性能问题:
class MySQLEngine(BaseEngine):
def filter_sql(self, sql="", limit_num=0):
"""
SQL语句安全过滤
:param sql: 原始SQL语句
:param limit_num: 限制行数
:return: 过滤后的SQL语句
"""
# 移除注释
sql = self.remove_comments(sql)
# 解析SQL类型
syntax_type = get_syntax_type(sql)
# SELECT语句处理
if syntax_type == "SELECT":
# 检查是否已包含LIMIT
if not re.search(r"\blimit\s+(\d+)(\s*,\s*(\d+))?\s*$", sql, re.I):
# 添加LIMIT限制
if limit_num > 0:
sql = f"{sql.rstrip(';')} LIMIT {limit_num}"
return sql
# 其他类型语句直接返回
return sql
查询执行流程与安全控制
Archery的查询执行流程包含了多重安全控制点,确保查询操作的安全性和稳定性:
数据脱敏与隐私保护
Archery提供了强大的数据脱敏功能,确保敏感数据不会泄露:
def data_masking(instance, db_name, sql, sql_result):
"""
数据脱敏处理
:param instance: 数据库实例
:param db_name: 数据库名称
:param sql: SQL语句
:param sql_result: 查询结果
:return: 脱敏后的结果
"""
# 获取脱敏规则配置
masking_rules = get_masking_rules(instance, db_name)
# 仅对SELECT语句进行脱敏
if get_syntax_type(sql) != "SELECT":
return sql_result
# 应用脱敏规则
for rule in masking_rules:
if rule["enable"]:
sql_result = apply_masking_rule(sql_result, rule)
return sql_result
def apply_masking_rule(resultset, rule):
"""
应用单个脱敏规则
:param resultset: 结果集
:param rule: 脱敏规则
:return: 脱敏后的结果集
"""
masked_result = resultset.copy()
# 正则表达式脱敏
if rule["method"] == "regex":
for i, row in enumerate(masked_result.rows):
for j, col in enumerate(row):
if should_mask_column(masked_result.column_list[j], rule):
masked_result.rows[i][j] = regex_mask(col, rule["pattern"])
# 哈希脱敏
elif rule["method"] == "hash":
for i, row in enumerate(masked_result.rows):
for j, col in enumerate(row):
if should_mask_column(masked_result.column_list[j], rule):
masked_result.rows[i][j] = hash_mask(col)
return masked_result
查询超时与连接管理
为防止长时间运行的查询影响数据库性能,Archery实现了查询超时控制机制:
def query(request):
"""
查询请求处理
"""
# 获取最大执行时间配置
max_execution_time = int(config.get("max_execution_time", 60))
# 创建kill连接定时任务
if thread_id:
schedule_name = f"query-{time.time()}"
run_date = datetime.datetime.now() + datetime.timedelta(seconds=max_execution_time)
add_kill_conn_schedule(schedule_name, run_date, instance.id, thread_id)
# 执行查询
with FuncTimer() as t:
query_result = query_engine.query(
db_name, sql_content, limit_num,
max_execution_time=max_execution_time * 1000
)
# 查询完成后取消kill任务
if thread_id:
del_schedule(schedule_name)
安全配置参数表
Archery提供了丰富的安全配置选项,管理员可以根据实际需求进行调整:
| 配置项 | 默认值 | 说明 | 影响范围 |
|---|---|---|---|
disable_star | False | 是否禁止使用SELECT * | 查询语句校验 |
max_execution_time | 60 | 查询最大执行时间(秒) | 查询超时控制 |
admin_query_limit | 5000 | 管理员查询行数限制 | 结果集大小 |
user_query_limit | 1000 | 普通用户查询行数限制 | 结果集大小 |
data_masking | False | 是否启用数据脱敏 | 隐私保护 |
query_check | True | 是否启用查询校验 | 安全控制 |
查询日志与审计追踪
所有查询操作都会被详细记录,便于审计和问题追踪:
class QueryLog(models.Model):
"""查询日志模型"""
username = models.CharField(max_length=100, verbose_name="用户名")
user_display = models.CharField(max_length=100, verbose_name="用户显示名")
instance_name = models.CharField(max_length=100, verbose_name="实例名")
db_name = models.CharField(max_length=100, verbose_name="数据库名")
sqllog = models.TextField(verbose_name="SQL语句")
effect_row = models.IntegerField(default=0, verbose_name="影响行数")
cost_time = models.FloatField(default=0, verbose_name="执行时间")
priv_check = models.BooleanField(default=True, verbose_name="权限校验")
hit_rule = models.CharField(max_length=100, blank=True, verbose_name="命中规则")
masking = models.BooleanField(default=False, verbose_name="是否脱敏")
create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
通过上述多层次的安全限制和控制机制,Archery确保了SQL查询操作的安全性、稳定性和合规性,为企业数据库管理提供了可靠保障。
查询结果集处理与数据脱敏
Archery作为企业级SQL审核查询平台,在数据安全方面提供了强大的数据脱敏功能,确保敏感信息在查询结果中不会直接暴露。本节将深入探讨Archery的查询结果集处理机制和数据脱敏实现原理。
数据脱敏架构设计
Archery的数据脱敏系统采用分层架构设计,主要包括三个核心组件:
脱敏规则配置管理
Archery支持灵活的脱敏规则配置,通过数据库表结构存储脱敏策略:
脱敏规则表结构
| 字段名 | 类型 | 说明 |
|---|---|---|
| rule_type | Integer | 规则类型标识 |
| rule_regex | Char(255) | 正则表达式模式 |
| hide_group | Integer | 需要隐藏的组号 |
| rule_desc | Char(100) | 规则描述 |
脱敏字段配置表结构
| 字段名 | 类型 | 说明 |
|---|---|---|
| rule_type | Integer | 关联的规则类型 |
| active | Boolean | 是否激活 |
| instance | ForeignKey | 关联的数据库实例 |
| table_schema | Char(64) | 数据库名 |
| table_name | Char(64) | 表名 |
| column_name | Char(64) | 字段名 |
查询处理流程
Archery的查询处理流程包含完整的脱敏检查机制:
核心脱敏算法实现
Archery的数据脱敏核心算法位于sql/utils/data_masking.py,主要包含以下几个关键函数:
1. 主脱敏函数 data_masking()
def data_masking(instance, db_name, sql, sql_result):
"""脱敏数据主函数"""
try:
# 解析查询语句,判断UNION需要单独处理
p = sqlparse.parse(sql)[0]
for token in p.tokens:
if token.ttype is Keyword and token.value.upper() in ["UNION", "UNION ALL"]:
keywords_count["UNION"] = keywords_count.get("UNION", 0) + 1
# 通过goInception获取select list
inception_engine = GoInceptionEngine()
select_list = inception_engine.query_data_masking(
instance=instance, db_name=db_name, sql=sql
)
# 分析语法树获取命中脱敏规则的列数据
hit_columns = analyze_query_tree(select_list, instance)
sql_result.mask_rule_hit = True if hit_columns else False
# 对命中规则列的数据进行脱敏
if hit_columns and sql_result.rows:
rows = list(sql_result.rows)
for column in hit_columns:
index, rule_type = column["index"], column["rule_type"]
masking_rule = masking_rules.get(rule_type)
# 应用正则脱敏
for idx, item in enumerate(rows):
rows[idx] = list(item)
rows[idx][index] = regex(masking_rule, rows[idx][index])
sql_result.rows = rows
sql_result.is_masked = True
except Exception as msg:
logger.warning(f"数据脱敏异常,错误信息:{traceback.format_exc()}")
sql_result.error = str(msg)
sql_result.status = 1
return sql_result
2. 正则脱敏函数 regex()
def regex(masking_rule, value):
"""利用正则表达式脱敏数据"""
if not value:
return value
rule_regex = masking_rule["rule_regex"]
hide_group = masking_rule["hide_group"]
# 系统通用规则正则表达式(动态生成)
if rule_type == 100 and isinstance(value, str):
value_average = math.floor(len(value) / 3)
value_remainder = len(value) % 3
# 动态生成三段式正则表达式
rule_regex = f"^([\\s\\S]{{{value_average},}}?)([\\s\\S]{{{value_average + (1 if value_remainder > 0 else 0)},}}?)([\\s\\S]{{{value_average + (1 if value_remainder > 1 else 0)},}}?)$"
# 正则匹配和替换
try:
p = re.compile(rule_regex, re.I)
m = p.search(str(value))
masking_str = ""
if m is None:
return value
for i in range(m.lastindex):
if i == hide_group - 1:
group = "*" * len(m.group(i + 1)) # 长度还原的脱敏
else:
group = m.group(i + 1)
masking_str = masking_str + group
return masking_str
except AttributeError:
return value
脱敏规则类型示例
Archery支持多种脱敏规则类型,以下是常见的配置示例:
1. 手机号脱敏规则
# 规则配置
rule_regex = "^([0-9]{3})([0-9]{4})([0-9]{4})$"
hide_group = 2 # 隐藏中间4位
# 脱敏效果
原始数据: 13812345678
脱敏后: 138****5678
2. 身份证号脱敏规则
# 规则配置
rule_regex = "^([0-9]{6})([0-9]{8})([0-9Xx]{4})$"
hide_group = 2 # 隐藏中间8位生日
# 脱敏效果
原始数据: 110101199001011234
脱敏后: 110101********1234
3. 邮箱地址脱敏规则
# 规则配置
rule_regex = "^([^@]+)(@[^@]+\\.[^@]+)$"
hide_group = 1 # 隐藏用户名部分
# 脱敏效果
原始数据: example@domain.com
脱敏后: ****@domain.com
UNION查询特殊处理
对于包含UNION的复杂查询,Archery提供了专门的去重处理机制:
def del_repeat(select_list, keywords_count):
"""处理UNION查询的字段去重"""
df = pd.DataFrame(select_list)
# 从原来的库、表、字段去重改为字段去重
result_index = df.groupby(["field"]).filter(lambda g: len(g) > 1).to_dict("records")
result_len = len(result_index)
# 计算取列表前多少的值=重复数量/(union次数+1)
group_count = int(result_len / (keywords_count["UNION"] + 1))
result = result_index[:group_count]
return result
性能优化策略
Archery在数据脱敏方面采用了多项性能优化措施:
- 批量预加载:一次性获取实例全部激活的脱敏字段信息,减少循环查询
- 缓存机制:对解析后的正则表达式进行缓存,避免重复编译
- 智能匹配:支持通配符匹配(
*-*-fieldname),减少精确匹配的开销 - 错误降级:脱敏失败时根据系统配置决定是否放行原始数据
配置管理界面
通过Archery的管理界面,可以方便地配置脱敏规则:
| 配置项 | 说明 | 示例值 |
|---|---|---|
| 规则类型 | 脱敏规则标识 | 100(通用规则) |
| 正则表达式 | 匹配模式 | ^([\s\S]{0,}?)([\s\S]{0,}?)([\s\S]{0,}?)$ |
| 隐藏组 | 需要脱敏的组号 | 2 |
| 规则描述 | 规则说明 | 三段式通用脱敏规则 |
实际应用场景
场景1:客户信息查询脱敏
-- 原始查询
SELECT customer_name, phone_number, id_card FROM customers WHERE status = 'active';
-- 脱敏后结果
+---------------+--------------+------------------+
| customer_name | phone_number | id_card |
+---------------+--------------+------------------+
| 张三 | 138****5678 | 110101********1234 |
| 李四 | 139****8765 | 110102********5678 |
+---------------+--------------+------------------+
场景2:员工薪资信息脱敏
-- 原始查询
SELECT employee_id, name, salary, bank_account FROM employees;
-- 脱敏后结果
+-------------+--------+--------+----------------+
| employee_id | name | salary | bank_account |
+-------------+--------+--------+----------------+
| 1001 | 王五 | **** | 6222********1234 |
| 1002 | 赵六 | **** | 6222********5678 |
+-------------+--------+--------+----------------+
安全审计与日志记录
所有脱敏操作都会生成详细的审计日志:
# 查询日志记录脱敏信息
query_log = QueryLog(
username=user.username,
user_display=user.display,
db_name=db_name,
instance_name=instance.instance_name,
sqllog=sql_content,
effect_row=limit_num,
cost_time=query_result.query_time,
priv_check=priv_check,
hit_rule=query_result.mask_rule_hit, # 命中规则标识
masking=query_result.is_masked, # 是否脱敏
)
query_log.save()
通过完善的查询结果集处理和数据脱敏机制,Archery确保了在企业环境中数据查询的安全性和合规性,既满足了业务查询需求,又保护了敏感信息不被泄露。
数据字典自动生成与导出
Archery作为一款专业的SQL审核查询平台,其数据字典功能为数据库管理提供了强大的元数据管理能力。通过自动化的数据字典生成与导出功能,用户可以轻松获取数据库结构的完整文档,便于团队协作、项目文档化和系统维护。
数据字典生成原理
Archery的数据字典生成基于其强大的数据库引擎抽象层,支持多种数据库类型(MySQL、MsSQL、Oracle等)。系统通过统一的接口获取数据库元数据信息,包括表结构、字段信息、索引详情等,然后将其转换为标准化的HTML格式文档。
核心功能实现
1. 表结构信息获取
Archery通过统一的get_tables_metas_data方法获取数据库表结构信息:
def get_tables_metas_data(self, db_name, **kwargs):
"""获取数据库所有表的元数据信息"""
# 实现细节:连接数据库,查询information_schema获取表结构信息
# 返回包含表名、字段、索引等完整信息的结构化数据
该方法返回的数据结构包含三个主要部分:
| 数据部分 | 描述 | 包含信息 |
|---|---|---|
| TABLE_INFO | 表基本信息 | 表名、注释、引擎类型等 |
| COLUMNS | 字段信息 | 字段名、类型、是否为空、默认值等 |
| ENGINE_KEYS | 引擎特定键信息 | 不同数据库引擎的特殊字段信息 |
2. 多数据库引擎支持
Archery支持多种数据库引擎的数据字典生成:
| 数据库类型 | 支持状态 | 特殊处理 |
|---|---|---|
| MySQL | ✅ 完全支持 | 支持SHOW CREATE TABLE语句 |
| MsSQL | ✅ 完全支持 | 使用系统视图获取元数据 |
| Oracle | ✅ 完全支持 | 使用数据字典视图 |
| PostgreSQL | ⚠️ 部分支持 | 基础表结构信息 |
| Redis | ❌ 不支持 | - |
| MongoDB | ❌ 不支持 | - |
3. 权限控制机制
数据字典导出功能具有严格的权限控制:
@permission_required("sql.data_dictionary_export", raise_exception=True)
def export(request):
# 普通用户只能导出自己有权限的单个数据库
# 管理员可以导出整个实例的所有数据库
权限控制矩阵:
| 用户类型 | 单个数据库导出 | 整个实例导出 | 下载权限 |
|---|---|---|---|
| 普通用户 | ✅ 允许 | ❌ 禁止 | ✅ 允许 |
| 管理员 | ✅ 允许 | ✅ 允许 | ✅ 允许 |
| 超级用户 | ✅ 允许 | ✅ 允许 | ✅ 允许 |
数据字典导出流程
步骤1:元数据采集
系统首先连接到目标数据库实例,通过查询系统表或信息模式来采集完整的元数据信息:
-- MySQL示例查询
SELECT
TABLE_NAME, TABLE_COMMENT, ENGINE, TABLE_COLLATION
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'database_name';
SELECT
COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_COMMENT
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'database_name';
步骤2:数据格式化
采集到的原始数据经过格式化处理,转换为统一的JSON结构:
{
"TABLE_INFO": {
"TABLE_NAME": "users",
"TABLE_COMMENT": "用户信息表",
"ENGINE": "InnoDB"
},
"COLUMNS": [
{
"COLUMN_NAME": "id",
"COLUMN_TYPE": "int(11)",
"IS_NULLABLE": "NO",
"COLUMN_KEY": "PRI",
"COLUMN_COMMENT": "主键ID"
}
],
"ENGINE_KEYS": [
{"key": "COLUMN_NAME", "value": "字段名"},
{"key": "COLUMN_TYPE", "value": "类型"}
]
}
步骤3:HTML模板渲染
使用Django模板引擎将格式化后的数据渲染为美观的HTML文档:
<table border="1" cellspacing="0" cellpadding="0" align="center">
<caption>表名:users</caption>
<caption>注释:用户信息表</caption>
<tbody>
<tr>
<th>字段名</th>
<th>类型</th>
<th>允许空值</th>
<th>默认值</th>
<th>注释</th>
</tr>
<tr>
<td>id</td>
<td>int(11)</td>
<td>NO</td>
<td>NULL</td>
<td>主键ID</td>
</tr>
</tbody>
</table>
步骤4:文件生成与下载
生成的HTML文件保存在服务器指定目录,并提供下载链接:
# 文件保存路径
path = os.path.join(settings.BASE_DIR, "downloads", "dictionary")
os.makedirs(path, exist_ok=True)
# 生成文件
with open(fullpath, "w", encoding="utf-8") as fp:
fp.write(html_content)
# 提供下载
response = FileResponse(open(fullpath, "rb"))
response["Content-Type"] = "application/octet-stream"
response["Content-Disposition"] = f'attachment;filename="{filename}.html"'
高级功能特性
1. 批量导出支持
管理员用户可以一次性导出整个数据库实例的所有数据字典,系统会自动为每个数据库生成单独的HTML文件:
if request.user.is_superuser:
dbs = query_engine.get_all_databases().rows
for db in dbs:
# 为每个数据库生成数据字典
table_metas = query_engine.get_tables_metas_data(db_name=db)
2. 安全路径验证
为防止路径遍历攻击,系统对导出路径进行严格验证:
def get_export_full_path(base_dir: str, instance_name: str, db_name: str) -> str:
"""验证实例名和数据库名是否安全"""
fullpath = os.path.normpath(
os.path.join(base_dir, f"{instance_name}_{db_name}.html")
)
if not fullpath.startswith(base_dir):
return "" # 路径不安全,返回空字符串
return fullpath
3. 多语言支持
数据字典支持中文显示,字段名和注释信息都会正确显示:
<!-- 中文字段名显示 -->
<th>字段名</th>
<th>类型</th>
<th>允许空值</th>
<th>默认值</th>
<th>注释</th>
4. 时间戳记录
每个导出的数据字典都包含生成时间戳,便于版本管理和追踪:
<p style="text-align:center;margin:20px auto;">
生成时间:2024-01-15 14:30:25
</p>
使用场景示例
场景1:新成员入职培训
当新成员加入项目团队时,可以通过数据字典快速了解数据库结构:
- 登录Archery系统
- 进入数据字典功能页面
- 选择需要了解的数据库
- 导出HTML格式的数据字典
- 离线查阅和学习
场景2:项目文档编制
在项目交付或审计时,需要提供完整的数据库设计文档:
- 使用管理员账号登录
- 导出整个数据库实例的数据字典
- 将生成的HTML文件整理为项目文档的一部分
- 提供给客户或审计人员
场景3:数据库结构对比
在不同环境(开发、测试、生产)之间进行数据库结构对比:
- 分别导出各环境的数据字典
- 使用文本对比工具比较HTML文件
- 快速识别结构差异
性能优化建议
对于大型数据库实例,数据字典导出可能会消耗较多资源,建议:
- 分时段操作:在业务低峰期执行导出操作
- 分批处理:对于超大型数据库,可以按业务模块分批导出
- 缓存利用:对不经常变化的数据库结构,可以缓存导出结果
- 资源监控:监控系统资源使用情况,避免影响正常业务
常见问题处理
问题1:导出权限不足
症状:普通用户尝试导出整个实例时提示"仅管理员可以导出整个实例的字典信息"
解决方案:
- 申请管理员权限
- 或逐个导出有权限的数据库
问题2:中文乱码
症状:导出的HTML文件中中文字符显示为乱码
解决方案:
- 确保数据库字符集设置正确
- 检查系统语言环境配置
问题3:导出文件过大
症状:大型数据库导出的HTML文件体积过大,打开缓慢
解决方案:
- 使用文本编辑器打开(如VS Code、Sublime Text)
- 或按业务模块分批导出
通过Archery的数据字典自动生成与导出功能,数据库管理员和开发人员可以轻松获取完整的数据库结构文档,大大提高了团队协作效率和系统维护的便利性。
查询历史记录与收藏管理
在Archery数据库管理平台中,查询历史记录与收藏管理功能是提升开发效率和知识沉淀的重要模块。通过系统化的查询记录管理和智能收藏功能,用户可以快速复用历史查询、分享优秀SQL语句,并建立个人知识库体系。
查询历史记录核心功能
Archery的查询历史记录功能基于QueryLog数据模型构建,该模型详细记录了每次SQL查询的关键信息:
class QueryLog(models.Model):
"""
记录在线查询sql的日志
"""
instance_name = models.CharField("实例名称", max_length=50)
db_name = models.CharField("数据库名称", max_length=64)
sqllog = models.TextField("执行的查询语句")
effect_row = models.BigIntegerField("返回行数")
cost_time = models.CharField("执行耗时", max_length=10, default="")
username = models.CharField("操作人", max_length=30)
user_display = models.CharField("操作人中文名", max_length=50, default="")
priv_check = models.BooleanField("查询权限是否正常校验", choices=((False, "跳过"), (True, "正常")), default=False)
hit_rule = models.BooleanField("查询是否命中脱敏规则", choices=((False, "未命中/未知"), (True, "命中")), default=False)
masking = models.BooleanField("查询结果是否正常脱敏", choices=((False, "否"), (True, "是")), default=False)
favorite = models.BooleanField("是否收藏", choices=((False, "否"), (True, "是")), default=False)
alias = models.CharField("语句标识", max_length=64, default="", blank=True)
create_time = models.DateTimeField("操作时间", auto_now_add=True)
查询记录数据结构表
| 字段名 | 数据类型 | 描述 | 示例值 |
|---|---|---|---|
| instance_name | CharField(50) | 目标数据库实例名称 | mysql-prod-01 |
| db_name | CharField(64) | 数据库名称 | user_db |
| sqllog | TextField | 执行的SQL查询语句 | SELECT * FROM users WHERE status=1 |
| effect_row | BigIntegerField | 查询返回的行数 | 1500 |
| cost_time | CharField(10) | 查询执行耗时 | 2.5s |
| username | CharField(30) | 执行用户账号 | zhangsan |
| user_display | CharField(50) | 用户显示名称 | 张三 |
| priv_check | BooleanField | 权限校验状态 | True |
| hit_rule | BooleanField | 脱敏规则命中 | False |
| masking | BooleanField | 脱敏执行状态 | True |
| favorite | BooleanField | 收藏状态 | True |
| alias | CharField(64) | 语句别名标识 | 活跃用户查询 |
| create_time | DateTimeField | 创建时间 | 2024-01-15 10:30:25 |
查询历史管理流程
Archery通过querylog视图函数实现查询历史的管理和检索功能:
def _querylog(request):
"""
获取sql查询记录
"""
user = request.user
limit = int(request.GET.get("limit", 0))
offset = int(request.GET.get("offset", 0))
star = True if request.GET.get("star") == "true" else False
query_log_id = request.GET.get("query_log_id")
search = request.GET.get("search", "")
start_date = request.GET.get("start_date", "")
end_date = request.GET.get("end_date", "")
# 权限控制:管理员查看全部,普通用户查看自己的
filter_dict = dict()
if not (user.is_superuser or user.has_perm("sql.audit_user")):
filter_dict["username"] = user.username
# 收藏筛选
if star:
filter_dict["favorite"] = star
# 时间范围筛选
if start_date and end_date:
end_date = datetime.datetime.strptime(end_date, "%Y-%m-%d") + datetime.timedelta(days=1)
filter_dict["create_time__range"] = (start_date, end_date)
# 执行查询
sql_log = QueryLog.objects.filter(**filter_dict)
sql_log = sql_log.filter(
Q(sqllog__icontains=search) |
Q(user_display__icontains=search) |
Q(alias__icontains=search)
)
查询历史检索流程图
收藏管理功能实现
收藏功能通过favorite视图函数实现,支持用户对重要查询语句进行标记和管理:
@permission_required("sql.menu_sqlquery", raise_exception=True)
def favorite(request):
"""
收藏查询记录,并且设置别名
"""
query_log_id = request.POST.get("query_log_id")
alias = request.POST.get("alias", "")
star = request.POST.get("star")
try:
query_log = QueryLog.objects.get(id=query_log_id)
# 权限校验:只能操作自己的记录或管理员权限
if not (request.user.is_superuser or
request.user.has_perm("sql.audit_user") or
query_log.username == request.user.username):
return HttpResponse(
json.dumps({"status": 1, "msg": "没有权限操作该记录"}),
content_type="application/json"
)
# 更新收藏状态和别名
if star is not None:
query_log.favorite = True if star == "true" else False
if alias:
query_log.alias = alias
query_log.save()
result = {"status": 0, "msg": "ok"}
except QueryLog.DoesNotExist:
result = {"status": 1, "msg": "查询记录不存在"}
return HttpResponse(json.dumps(result), content_type="application/json")
收藏管理操作示例
-- 示例:收藏一个常用的用户查询
UPDATE query_log
SET favorite = TRUE, alias = '活跃用户统计'
WHERE id = 12345 AND username = 'current_user';
-- 示例:取消收藏
UPDATE query_log
SET favorite = FALSE, alias = ''
WHERE id = 12345;
高级查询功能
Archery提供丰富的查询历史筛选功能,支持多维度检索:
1. 时间范围查询
支持按日期范围筛选查询记录,精确到天级别:
# 时间范围筛选实现
start_date = "2024-01-01"
end_date = "2024-01-15"
filter_dict["create_time__range"] = (
datetime.datetime.strptime(start_date, "%Y-%m-%d"),
datetime.datetime.strptime(end_date, "%Y-%m-%d") + datetime.timedelta(days=1)
)
2. 多关键词搜索
支持在SQL语句、用户名称、别名等多个字段中进行模糊搜索:
search = "用户统计"
sql_log = sql_log.filter(
Q(sqllog__icontains=search) |
Q(user_display__icontains=search) |
Q(alias__icontains=search)
)
3. 收藏状态筛选
快速筛选出已收藏的查询语句:
star = True # 只显示收藏的记录
filter_dict["favorite"] = star
性能优化策略
为确保查询历史功能的高效运行,Archery采用了多种优化策略:
数据库索引优化
-- 为常用查询字段创建索引
CREATE INDEX idx_query_log_username ON query_log(username);
CREATE INDEX idx_query_log_create_time ON query_log(create_time);
CREATE INDEX idx_query_log_favorite ON query_log(favorite);
CREATE INDEX idx_query_log_instance ON query_log(instance_name);
分页查询机制
# 分页查询实现
limit = 50
offset = 0
sql_log_list = sql_log.order_by("-id")[offset:offset+limit].values(
"id", "instance_name", "db_name", "sqllog", "effect_row",
"cost_time", "user_display", "favorite", "alias", "create_time"
)
应用场景示例
场景1:快速复用历史查询
开发人员可以通过收藏功能标记常用的查询模板,后续直接复用:
-- 收藏的查询模板:用户活跃度分析
SELECT
DATE(create_time) as date,
COUNT(*) as new_users,
COUNT(CASE WHEN last_login_time > DATE_SUB(NOW(), INTERVAL 7 DAY) THEN 1 END) as active_users
FROM users
GROUP BY DATE(create_time)
ORDER BY date DESC;
场景2:团队知识共享
通过查询历史记录,团队成员可以学习优秀的SQL写法:
# 获取团队常用的查询模式
team_queries = QueryLog.objects.filter(
user_display__in=team_members,
favorite=True
).order_by('-create_time')[:10]
场景3:性能分析优化
通过分析历史查询的执行时间和返回行数,识别需要优化的SQL:
# 找出执行时间最长的查询
slow_queries = QueryLog.objects.filter(
cost_time__gt='5.0s'
).order_by('-cost_time')[:20]
安全与权限控制
Archery在查询历史管理中实施了严格的安全措施:
- 数据隔离:普通用户只能查看自己的查询记录
- 权限分级:管理员和审计员可以查看所有记录
- 操作限制:用户只能收藏和修改自己的查询记录
- 敏感信息保护:所有查询记录都经过脱敏处理
# 权限验证逻辑
if not (user.is_superuser or user.has_perm("sql.audit_user")):
filter_dict["username"] = user.username
通过完善的查询历史记录与收藏管理功能,Archery为用户提供了高效、安全、便捷的SQL查询管理体验,大大提升了数据库开发和管理的工作效率。
总结
Archery通过精细化的权限控制、SQL安全过滤、数据脱敏和完整的查询历史管理,构建了全方位的数据库查询安全体系。结合数据字典的自动生成与导出功能,它不仅保障了数据操作的安全性和合规性,还极大地提升了团队协作和系统维护的效率。这些功能使得Archery成为企业级数据库管理的可靠工具,适用于各种复杂的业务场景和安全要求。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



