基于Python flask-sqlalchemy的SQLServer数据库管理平台

news2025/2/19 15:48:26

适应场景:

主要用于帮助DBA自动化很多日常工作,包括:

  • 数据库状态监控
  • 性能问题诊断
  • 日志分析
  • 自动巡检
  • 问题告警

系统截图:

main.py

from flask import Blueprint, render_template, request, flash, redirect, url_for
from flask_login import login_required
from app.models.datasource import DataSource
from app import db

bp = Blueprint('main', __name__)

@bp.route('/')
@login_required
def index():
    datasources = DataSource.query.all()
    return render_template('index.html', datasources=datasources)

@bp.route('/datasource', methods=['GET', 'POST'])
@login_required
def datasource_list():
    datasources = DataSource.query.all()
    return render_template('datasource/list.html', datasources=datasources)

@bp.route('/datasource/add', methods=['GET', 'POST'])
@login_required
def datasource_add():
    if request.method == 'POST':
        try:
            datasource = DataSource(
                name=request.form['name'],
                host=request.form['host'],
                port=int(request.form['port']),
                database=request.form['database'],
                username=request.form['username'],
                password=request.form['password']
            )
            db.session.add(datasource)
            db.session.commit()
            flash('数据源添加成功', 'success')
            return redirect(url_for('main.datasource_list'))
        except Exception as e:
            flash(f'添加失败: {str(e)}', 'danger')
    return render_template('datasource/form.html')

@bp.route('/datasource/edit/<int:id>', methods=['GET', 'POST'])
@login_required
def datasource_edit(id):
    datasource = DataSource.query.get_or_404(id)
    if request.method == 'POST':
        try:
            datasource.name = request.form['name']
            datasource.host = request.form['host']
            datasource.port = int(request.form['port'])
            datasource.database = request.form['database']
            datasource.username = request.form['username']
            if request.form['password']:  # 只在提供新密码时更新
                datasource.password = request.form['password']
            db.session.commit()
            flash('数据源更新成功', 'success')
            return redirect(url_for('main.datasource_list'))
        except Exception as e:
            flash(f'更新失败: {str(e)}', 'danger')
    return render_template('datasource/form.html', datasource=datasource)

@bp.route('/datasource/delete/<int:id>')
@login_required
def datasource_delete(id):
    datasource = DataSource.query.get_or_404(id)
    try:
        db.session.delete(datasource)
        db.session.commit()
        flash('数据源删除成功', 'success')
    except Exception as e:
        flash(f'删除失败: {str(e)}', 'danger')
    return redirect(url_for('main.datasource_list'))

@bp.route('/datasource/toggle/<int:id>')
@login_required
def datasource_toggle(id):
    datasource = DataSource.query.get_or_404(id)
    try:
        datasource.is_active = not datasource.is_active
        db.session.commit()
        flash('状态更新成功', 'success')
    except Exception as e:
        flash(f'更新失败: {str(e)}', 'danger')
    return redirect(url_for('main.datasource_list')) 

monitor.py

from flask import Blueprint, render_template, jsonify
from flask_login import login_required
from app.models.datasource import DataSource
from app.services.monitor_service import MonitorService

bp = Blueprint('monitor', __name__, url_prefix='/monitor')

@bp.route('/status/<int:id>')
@login_required
def status(id):
    datasource = DataSource.query.get_or_404(id)
    status = MonitorService.get_database_status(id)
    return render_template('monitor/status.html', datasource=datasource, status=status)

@bp.route('/api/status/<int:id>')
@login_required
def api_status(id):
    status = MonitorService.get_database_status(id)
    return jsonify(status) 

monitor_service.py

# import pyodbc  # 暂时注释掉
from app.models.datasource import DataSource
import datetime

class MonitorService:
    @staticmethod
    def get_database_status(datasource_id):
        datasource = DataSource.query.get(datasource_id)
        if not datasource:
            return None
            
        # 模拟获取更丰富的数据库状态信息
        return {
            'basic_info': {
                'database_id': 1,
                'name': datasource.database,
                'state': 'ONLINE',
                'recovery_model': 'FULL',
                'compatibility_level': '150',
                'collation': 'Chinese_PRC_CI_AS',
                'created_time': '2023-01-01 08:00:00',
                'last_backup_time': '2024-03-10 03:00:00'
            },
            'size_info': {
                'data_size': '1024 MB',
                'log_size': '256 MB',
                'total_size': '1280 MB',
                'data_space_used': 75.5,  # 百分比
                'log_space_used': 45.2,   # 百分比
                'unallocated_space': '512 MB'
            },
            'performance': {
                'cpu_usage': 35.5,        # 百分比
                'memory_usage': 4096,      # MB
                'buffer_cache_hit': 98.5,  # 百分比
                'page_life_expectancy': 1200,  # 秒
                'batch_requests': 450,     # 每秒
                'user_connections': 85,
                'active_transactions': 12,
                'blocked_processes': 0,
                'deadlocks': 0,
                'lock_waits': 2
            },
            'io_stats': {
                'reads_per_sec': 250,
                'writes_per_sec': 120,
                'io_pending': 0,
                'io_stall_ms': 150,
                'read_latency_ms': 3,
                'write_latency_ms': 5
            },
            'availability': {
                'uptime': '15 days 6 hours',
                'last_restart': '2024-02-25 00:00:00',
                'failovers_last_24h': 0,
                'mirror_status': 'Not Configured'
            },
            'alerts': [
                {
                    'type': 'warning',
                    'message': '数据文件空间使用率超过75%',
                    'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                }
            ]
        }

    @staticmethod
    def get_database_status_real(datasource_id):
        datasource = DataSource.query.get(datasource_id)
        if not datasource:
            return None
            
        conn_str = (
            f"DRIVER={{ODBC Driver 17 for SQL Server}};"
            f"SERVER={datasource.host},{datasource.port};"
            f"DATABASE={datasource.database};"
            f"UID={datasource.username};"
            f"PWD={datasource.password}"
        )
        
        try:
            conn = pyodbc.connect(conn_str)
            cursor = conn.cursor()
            
            # 获取数据库状态
            cursor.execute("""
                SELECT 
                    database_id,
                    name,
                    state_desc,
                    recovery_model_desc,
                    total_size = str(size*8/1024)+' MB'
                FROM sys.databases
                WHERE name = ?
            """, datasource.database)
            
            status = cursor.fetchone()
            
            # 获取性能指标
            cursor.execute("""
                SELECT TOP 1
                    cpu_time,
                    total_worker_time,
                    total_physical_reads,
                    total_logical_writes,
                    total_logical_reads
                FROM sys.dm_exec_query_stats
                ORDER BY total_worker_time DESC
            """)
            
            performance = cursor.fetchone()
            
            return {
                'status': {
                    'database_id': status[0],
                    'name': status[1],
                    'state': status[2],
                    'recovery_model': status[3],
                    'size': status[4]
                },
                'performance': {
                    'cpu_time': performance[0],
                    'worker_time': performance[1],
                    'physical_reads': performance[2],
                    'logical_writes': performance[3],
                    'logical_reads': performance[4]
                }
            }
            
        except Exception as e:
            return {'error': str(e)}
        finally:
            if 'conn' in locals():
                conn.close() 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2298643.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

npm运行Vue项目报错 error:0308010c:digital envelope routines::unsupported

大家好&#xff0c;我是 程序员码递夫。 问题 VSCode 运行Vue项目&#xff0c;提示错误&#xff1a; building 2/2 modules 0 activeError: error:0308010c:digital envelope routines::unsupported 解决方法 原因是 npm 高版本(大于17)&#xff0c;对ssl的处理做了改进&…

计数排序

目录 计数排序原理和步骤&#xff1a; 完整代码实现&#xff1a; 计数排序原理和步骤&#xff1a; 当一段数据比较集中在一个范围&#xff0c;比如 98&#xff0c;95&#xff0c;98&#xff0c;91&#xff0c;90&#xff0c;93&#xff0c;94&#xff0c;97&#xff0c;93&…

Pythong 解决Pycharm 运行太慢

Pythong 解决Pycharm 运行太慢 官方给Pycharm自身占用的最大内存设低估了限制,我的Pycharm刚开始默认是256mb。 首先找到自己的Pycharm安装目录 根据合适自己的改 保存&#xff0c;重启Pycharm

fastadmin 接口请求提示跨域

问题描述 小程序项目&#xff0c;内嵌h5页面&#xff0c;在h5页面调用后端php接口&#xff0c;提示跨域。网上查找解决方案如下&#xff1a; 1&#xff0c;设置header // 在入口文件index.php直接写入直接写入 header("Access-Control-Allow-Origin:*"); header(&q…

NHANES指标推荐:DDA!

文章题目&#xff1a;Association of dietary decanoic acid intake with diabetes or prediabetes: an analysis from NHANES 2005-2016 DOI&#xff1a;10.3389/fnut.2024.1483045 中文标题&#xff1a;饮食中癸酸摄入量与糖尿病或糖尿病前期的关系&#xff1a;2005-2016 年 …

用大模型学大模型04-模型与网络

目前已经学完深度学习的数学基础&#xff0c;开始学习各种 模型和网络阶段&#xff0c;给出一个从简单到入门的&#xff0c;层层递进的学习路线。并给出学习每种模型需要的前置知识。增加注意力机制&#xff0c;bert, 大模型&#xff0c;gpt, transformer&#xff0c; MOE等流行…

PostgreSQL 数据库压力测试指南

一、为什么需要压力测试&#xff1f; 数据库需要进行压力测试的原因主要包括以下几个方面&#xff1a; 性能评估&#xff1a;通过压力测试&#xff0c;可以了解数据库在高负载情况下的性能表现&#xff0c;包括响应时间、吞吐量和资源利用率等。这有助于确定系统的性能瓶颈。 …

Python----PyQt开发(PyQt高级:组件大小,界面位置,按钮,文本显示,文本输入,字体大小)

一、大小 setMinimumSize(width, height) 描述: 设置控件的最小尺寸。控件不会被缩小到比这个尺寸更小的大小。 参数: width: 最小宽度&#xff08;以像素为单位&#xff09;。 height: 最小高度&#xff08;以像素为单位&#xff09;。 button.setMinimumSize(100, …

qt + opengl 给立方体增加阴影

在前几篇文章里面学会了通过opengl实现一个立方体&#xff0c;那么这篇我们来学习光照。 风氏光照模型的主要结构由3个分量组成&#xff1a;环境(Ambient)、漫反射(Diffuse)和镜面(Specular)光照。下面这张图展示了这些光照分量看起来的样子&#xff1a; 1 环境光照(Ambient …

vue2老版本 npm install 安装失败_安装卡主

vue2老版本 npm install 安装失败_安装卡主 特别说明&#xff1a;vue2老版本安装慢、运行慢&#xff0c;建议升级vue3element plus vite 解决方案1&#xff1a; 第一步、修改npm 镜像为国内镜像 使用淘宝镜像&#xff1a; npm config set registry https://registry.npmmir…

20250213编译飞凌的OK3588-C_Linux5.10.209+Qt5.15.10_用户资料_R1

20250213编译飞凌的OK3588-C_Linux5.10.209Qt5.15.10_用户资料_R1 2025/2/13 11:43 缘起&#xff1a;飞凌发布了高版本内核的适配OK3588-C的Buildroot的SDK&#xff1a;OK3588-C_Linux5.10.209Qt5.15.10_用户资料_R1。 但是编译异常了。 于是按照百度升级libc6&#xff0c;可以…

中望CAD c#二次开发 ——VS环境配置

新建类库项目&#xff1a;下一步 下一步 下一步&#xff1a; 或直接&#xff1a; 改为&#xff1a; <Project Sdk"Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>NET48</TargetFramework> <LangVersion>pr…

Rander压力测试监测,更改服务端资源node

测试策略 压力测试&#xff0c; 目前是本地VM的资源不够&#xff0c;导致压力瓶颈&#xff0c;目前本地的VM&#xff0c;CPU是6个&#xff0c;可以增加到8个&#xff0c;服务端目前资源利用率没有达到最高点 we are now using 3 nodes with 3 pods, therefore, we need the …

Go语言实现十大排序算法超细节图片讲解

基础排序 冒泡排序 将序列中的元素进行两两比较&#xff0c;将大的元素移动到序列的末尾。 平均时间复杂度是O(n^2)&#xff0c;最坏时间复杂度是O(n^2)&#xff0c;最好时间复杂度是O(n)&#xff0c;排序结果具有稳定性&#xff0c;空间复杂度是O(1)。 这里所说的稳定性是针对…

【鸿蒙Next】写入沙箱的日志文件如何查看

demo案例&#xff1a;https://gitee.com/pengyoucongcode/TxtEdit 文章参考&#xff1a;https://blog.csdn.net/qq_42896653/article/details/144782468

网页五子棋——通用模块

目录 项目创建 通用功能模块 错误码 自定义异常类 CommonResult jackson 加密工具 项目创建 使用 idea 创建 SpringBoot 项目&#xff0c;并引入相关依赖&#xff1a; 配置 MyBatis&#xff1a; 编辑 application.yml&#xff1a; spring:datasource: # 数据库连接配…

第6章 6.2使用ASP.NET Core 开发WebAPI ASP.NET Core Web API

6.2.1 Web API项目的搭建 进入VS&#xff0c;【创建新项目】&#xff0c;选择【ASP.NET Core Web API】模板&#xff0c;【下一步】&#xff0c;编辑项目名称及项目位置&#xff0c;【下一步】&#xff0c;选择框架&#xff0c;其他选项默认即可&#xff0c;【创建】。 进入项…

[MFC] 使用控件

介绍如何使用控件&#xff0c;以及如何获取控件中的数值 check Box 添加点击事件&#xff0c;即选中和取消选中触发的事件 第一种方式是按照如下方式第二种方式是直接双击点击进去 void CMFCApplication1Dlg::OnBnClickedCheckSun() {// TODO: 在此添加控件通知处理程序代…

景联文科技:以精准标注赋能AI未来,打造高质量数据基石

在人工智能蓬勃发展的时代&#xff0c;数据已成为驱动技术革新的核心燃料&#xff0c;而高质量的数据标注则是让AI模型从“感知”走向“认知”的关键桥梁。作为深耕数据服务领域的创新者&#xff0c;景联文科技始终以“精准、高效、安全”为核心理念&#xff0c;为全球AI企业提…

2月14(信息差)

&#x1f30d;杭州&#xff1a;全球数贸港核心区建设方案拟出台 争取国家支持杭州在网络游戏管理给予更多权限 &#x1f384;Kimi深夜炸场&#xff1a;满血版多模态o1级推理模型&#xff01;OpenAI外全球首次&#xff01;Jim Fan&#xff1a;同天两款国产o1绝对不是巧合&#x…