Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

添加慢SQL的的优化管理功能 #2549

Open
wants to merge 23 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dev-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ pytest-django
pytest-mock
pytest-cov
codecov
flake8
flake8
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ omit = [
# omit this single file
"sql/migrations/*",
"venv*"
]
]
1 change: 1 addition & 0 deletions sql/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,7 @@ class SlowQuery(models.Model):
reviewed_by = models.CharField(max_length=20, blank=True, null=True)
reviewed_on = models.DateTimeField(blank=True, null=True)
comments = models.TextField(blank=True, null=True)
reviewed_status = models.CharField(max_length=24, blank=True, null=True)

class Meta:
managed = False
Expand Down
69 changes: 66 additions & 3 deletions sql/slowlog.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@
from django.contrib.auth.decorators import permission_required
from django.db.models import F, Sum, Value as V, Max
from django.db.models.functions import Concat
from django.http import HttpResponse
from django.http import HttpResponse, JsonResponse
from django.views.decorators.cache import cache_page
from pyecharts.charts import Line
from pyecharts import options as opts
from common.utils.chart_dao import ChartDao
from sql.engines import get_engine

from sql.utils.resource_group import user_instances
from sql.utils.instance_management import (
SUPPORTED_MANAGEMENT_DB_TYPE,
)
from common.utils.extend_json_encoder import ExtendJSONEncoder
from .models import Instance, SlowQuery, SlowQueryHistory, AliyunRdsConfig

Expand Down Expand Up @@ -68,8 +71,22 @@ def slowquery_review(request):
fingerprint__icontains=search,
**filter_kwargs
)
.annotate(SQLText=F("fingerprint"), SQLId=F("checksum"))
.values("SQLText", "SQLId")
.annotate(
SQLText=F("fingerprint"),
SQLId=F("checksum"),
ReviewedBy=F("reviewed_by"),
ReviewedOn=F("reviewed_on"),
Comments=F("comments"),
ReviewedStatus=F("reviewed_status"),
)
.values(
"SQLText",
"SQLId",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个PR中,SQLId, checksum, SQLChecksum实际上都是archery.mysql_slow_query_review表的checksum字段。是为了显示方便所以有不同叫法。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

review_checksum也是

"ReviewedBy",
"ReviewedOn",
"Comments",
"ReviewedStatus",
)
.annotate(
CreateTime=Max("slowqueryhistory__ts_max"),
DBName=Max("slowqueryhistory__db_max"), # 数据库
Expand Down Expand Up @@ -162,6 +179,7 @@ def slowquery_review_history(request):
sample__icontains=search,
**filter_kwargs
).annotate(
SQLChecksum=F("checksum"), # SQL语句校验和
ExecutionStartTime=F(
"ts_min"
), # 本次统计(每5分钟一次)该类型sql语句出现的最小时间
Expand All @@ -182,6 +200,7 @@ def slowquery_review_history(request):
slow_sql_record_list = slow_sql_record_obj.order_by(
"-" + sortName if "desc".__eq__(sortOrder) else sortName
)[offset:limit].values(
"SQLChecksum",
"ExecutionStartTime",
"DBName",
"HostAddress",
Expand Down Expand Up @@ -251,3 +270,47 @@ def report(request):

result = {"status": 0, "msg": "", "data": line.render_embed()}
return HttpResponse(json.dumps(result), content_type="application/json")


@permission_required("sql.menu_slowquery", raise_exception=True)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

沿用查看慢日志的权限,没有单独加权限控制,后续可以添加。

def listreview(request):
"""获取优化详情"""
checksum = request.POST.get("review_checksum")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

获取用户在页面中勾选的SQL语句对应的checksum

if not checksum:
return JsonResponse({"status": 0, "msg": "Checksum获取失败", "data": []})
review_details = SlowQuery.objects.filter(checksum=checksum).values(
"checksum", "reviewed_by", "reviewed_on", "comments", "reviewed_status"
)
rows = [row for row in review_details]
if not rows:
result = {"status": 1, "msg": "优化详情获取失败"}
else:
result = {"status": 0, "msg": "ok", "rows": rows}
return HttpResponse(
json.dumps(result, cls=ExtendJSONEncoder, bigint_as_string=True),
content_type="application/json",
)


@permission_required("sql.menu_slowquery", raise_exception=True)
def editreview(request):
"""更新优化详情"""
checksum = request.POST.get("checksum")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

获取欲操作的SQL语句对应的checksum

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

编辑框传过来的值

reviewed_by = request.POST.get("reviewed_by", "")
reviewed_on = request.POST.get("reviewed_on", "")
comments = request.POST.get("comments", "")
reviewed_status = request.POST.get("reviewed_status", "")
if not all([checksum, reviewed_by, reviewed_on, comments, reviewed_status]):
return JsonResponse(
{"status": 1, "msg": "参数不完整,请确认后提交", "data": []}
)
record = SlowQuery.objects.filter(checksum=checksum)
if not record:
return JsonResponse({"status": 1, "msg": "记录不存在", "data": []})
record.update(
reviewed_by=reviewed_by,
reviewed_on=reviewed_on,
comments=comments,
reviewed_status=reviewed_status,
)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

更新数据库

return JsonResponse({"status": 0, "msg": "", "data": []})
45 changes: 45 additions & 0 deletions sql/templates/slowquery.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<!-- 自定义操作按钮-->
<div id="sqladvisor-toolbar" class="btn-group right" style="display:none;">
<button id=sqladvisor class="btn btn-default" style="" type="button">获取优化建议</button>
<button id=sqlreview class="btn btn-default" style="" type="button">编辑优化详情</button>
</div>
<!-- Nav tabs -->
<ul id="nav-tabs" class="nav nav-tabs" role="tablist">
Expand Down Expand Up @@ -269,6 +270,25 @@
}
});
});

//点击编辑按钮编辑优化详情
$(function () {
$("#sqlreview").click(function () {
if ($("#slowsqlinfo-list").bootstrapTable('getSelections')[0]) {
var review_sql_content = $("#slowsqlinfo-list").bootstrapTable('getSelections')[0]['SQLText'] + ';';
var review_db_name = $("#slowsqlinfo-list").bootstrapTable('getSelections')[0]['DBName'];
var review_instance_name = $("#instance_name").val();
var review_checksum = $("#slowsqlinfo-list").bootstrapTable('getSelections')[0]['SQLChecksum']
sessionStorage.setItem('review_sql_content ', review_sql_content );
sessionStorage.setItem('review_db_name', review_db_name);
sessionStorage.setItem('review_instance_name', review_instance_name);
sessionStorage.setItem('review_checksum', review_checksum);
location.href = '/sqlreview/';
} else {
alert("请先选择需要优化的sql")
}
});
});
</script>
<!-- 获取列表数据 -->
<script>
Expand Down Expand Up @@ -427,6 +447,31 @@
title: '平均返回行数',
field: 'ReturnRowAvg',
sortable:true
}, {
title: '优化内容',
field: 'Comments',
formatter: function (value, row, index) {
if (value != null) {
if (value.length > 20) {
var sql = value.substr(0, 20) + '...';
return sql;
} else {
return value
}
}
}
}, {
title: '优化状态',
field: 'ReviewedStatus',
sortable:true
}, {
title: '提交人',
field: 'ReviewedBy',
sortable:true
}, {
title: '提交日期',
field: 'ReviewedOn',
sortable:true
}],
locale: 'zh-CN',
onLoadError: onLoadErrorCallback,
Expand Down
188 changes: 188 additions & 0 deletions sql/templates/sqlreview.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
{% extends "base.html" %}




{% block content %}

<!-- 表格-->
<div class="table-responsive">
<table id="user-list" data-toggle="table" class="table table-striped table-hover"
style="table-layout:inherit;overflow:hidden;text-overflow:ellipsis;">
</table>
</div>
<!-- 修改密码模态框 -->
<div class="modal fade" id="edit-review" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">&times;</span></button>
<h4 class="modal-title">编辑详情</h4>
</div>
<div class="modal-body form-group">
<div class="form-group row">
<label for="comments" class="col-sm-3 col-form-label">
<span style="color:red">*</span>内容</label>
<div class="col-sm-9">
<input type="text" id="comments" class="form-control"
placeholder="请填写详情">
</small>
</div>
</div>
<div class="form-group row">
<label for="reviewed-status" class="col-sm-3 col-form-label">
<span style="color:red">*</span>状态</label>
<div class="col-sm-9">
<select id="reviewed-status" name="reviewed-status-name">
<option value="open">open</option>
<option value="resolved">resolved</option>
<option value="pending">pending</option>
<option value="closed">closed</option>
</select>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-danger" id="editRevBtn">修改</button>
</div>
</div>
</div>
</div>

{% endblock content %}
{% block js %}
{% load static %}
<script src="{% static 'bootstrap-table/js/bootstrap-table-export.min.js' %}"></script>
<script src="{% static 'bootstrap-table/js/tableExport.min.js' %}"></script>
<script>
//获取优化详情列表
function review_list() {
var pathname = window.location.pathname;
if (pathname == "/sqlreview/") {
var review_checksum = sessionStorage.getItem('review_checksum');
}
//初始化table
$('#user-list').bootstrapTable('destroy').bootstrapTable({
escape: false,
method: 'post',
contentType: "application/x-www-form-urlencoded",
url: "/sqlreview/list/",
striped: true, //是否显示行间隔色
cache: false, //是否使用缓存,默认为true,所以一般情况下需要设置一下这个属性(*)
pagination: true, //是否显示分页(*)
sortable: true, //是否启用排序
sortOrder: "asc", //排序方式
sidePagination: "client", //分页方式:client客户端分页,server服务端分页(*)
pageNumber: 1, //初始化加载第一页,默认第一页,并记录
pageSize: 14, //每页的记录行数(*)
pageList: [20, 30, 50, 100], //可供选择的每页的行数(*)
showExport: true, //是否显示导出按钮
exportOptions: {
fileName: 'checksum' //文件名称设置
},
search: true, //是否显示表格搜索
strictSearch: false, //是否全匹配搜索
showColumns: true, //是否显示所有的列(选择显示的列)
showRefresh: true, //是否显示刷新按钮
minimumCountColumns: 2, //最少允许的列数
clickToSelect: true, //是否启用点击选中行
uniqueId: 'checksum', //每一行的唯一标识,一般为主键列
showToggle: true, //是否显示详细视图和列表视图的切换按钮
cardView: false, //是否显示详细视图
detailView: false, //是否显示父子表
locale: 'zh-CN', //本地化
toolbar: "#toolbar", //指明自定义的toolbar
queryParamsType: 'limit',
//请求服务数据时所传参数
queryParams:
function (params) {
if (review_checksum) {
return {
search: params.search,
review_checksum: review_checksum
}
}
},
columns: [{
title: 'SQLID',
field: 'checksum'
},{
title: '优化内容',
field: 'comments',
width: "30",
widthUnit: "%"
},{
title: '优化状态',
field: 'reviewed_status'
},{
title: '提交人',
field: 'reviewed_by'
},{
title: '提交日期',
field: 'reviewed_on'
},{
title: '操作',
field: '',
formatter: function (value, row, index) {
let btn_edit_review = "<button class=\"btn btn-warning btn-xs\" review_checksum=\"" + row.checksum + "\" onclick=\"review_edit(this)" + "\">编辑</button>\n";
return btn_edit_review
}
}],
onLoadSuccess: function (data) {
if (data.status !== 0) {
alert("数据加载失败!" + data.msg);
}},
onLoadError: onLoadErrorCallback,
onSearch: function (e) {
//传搜索参数给服务器
queryParams(e)
}
});

}

//编辑页面
function review_edit(obj) {
review_checksum = $(obj).attr("review_checksum");
user= user;
$("#edit-review").modal('show');
//编辑详情
$("#editRevBtn").unbind("click").click(function () {
var dt = new Date(+new Date()+8*3600*1000);
currenttime = dt.toISOString().slice(0, 19).replace('T', ' ');
$.ajax({
type: "post",
url: "/sqlreview/edit/",
dataType: "json",
data: {
checksum: review_checksum,
reviewed_by: user,
reviewed_on: currenttime,
comments: $("#comments").val(),
reviewed_status: $("#reviewed-status").val(),
},
complete: function () {
},
success: function (data) {
if (data.status === 0) {
$('#edit-review').modal('hide');
$("#edit-review input").val("");
review_list()
} else {
alert(data.msg);
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(errorThrown);
}
});
});
}
//初始化数据
$(document).ready(function () {
review_list();
});
</script>
{% endblock %}
Loading
Loading