From 50564f852235d24e76234b34144a451b7d24b1fc Mon Sep 17 00:00:00 2001 From: GreatSQL Date: Thu, 21 Aug 2025 17:52:05 +0800 Subject: [PATCH 1/7] =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=94=B9=E5=90=8DGreatSq?= =?UTF-8?q?lMcpApplication=20=3D>=20GreatSQLMCPApplication?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../greatsqlmcp/GreatSQLMCPApplication.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/main/java/org/greatsql/greatsqlmcp/GreatSQLMCPApplication.java diff --git a/src/main/java/org/greatsql/greatsqlmcp/GreatSQLMCPApplication.java b/src/main/java/org/greatsql/greatsqlmcp/GreatSQLMCPApplication.java new file mode 100644 index 0000000..fedd83a --- /dev/null +++ b/src/main/java/org/greatsql/greatsqlmcp/GreatSQLMCPApplication.java @@ -0,0 +1,22 @@ +package org.greatsql.greatsqlmcp; + +import org.greatsql.greatsqlmcp.service.DatabaseService; +import org.springframework.ai.tool.ToolCallback; +import org.springframework.ai.tool.ToolCallbacks; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import java.util.List; + +@SpringBootApplication +public class GreatSQLMCPApplication { + + public static void main(String[] args) { + SpringApplication.run(GreatSQLMCPApplication.class, args); + } + + @Bean + public List getToolCallbacks(DatabaseService databaseService) { + return List.of(ToolCallbacks.from(databaseService)); + } +} -- Gitee From ec78e790d445fde56aec80cddfb972fed045fab9 Mon Sep 17 00:00:00 2001 From: GreatSQL Date: Thu, 21 Aug 2025 17:53:31 +0800 Subject: [PATCH 2/7] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=96=B0=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=EF=BC=9A=E6=A3=80=E6=9F=A5=E6=98=AF=E5=90=A6=E6=9C=89?= =?UTF-8?q?=E5=A4=A7=E4=BA=8B=E5=8A=A1=E3=80=81=E9=95=BF=E4=BA=8B=E5=8A=A1?= =?UTF-8?q?=E7=AD=89=E9=9C=80=E8=A6=81=E7=89=B9=E5=88=AB=E6=B3=A8=E6=84=8F?= =?UTF-8?q?=E7=9A=84=E4=BA=8B=E5=8A=A1=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../greatsqlmcp/GreatSqlMcpApplication.java | 22 --------- .../greatsqlmcp/controller/McpController.java | 42 +++++++++++++++- .../greatsqlmcp/service/DatabaseService.java | 48 ++++++++++++++++++- 3 files changed, 88 insertions(+), 24 deletions(-) delete mode 100644 src/main/java/org/greatsql/greatsqlmcp/GreatSqlMcpApplication.java diff --git a/src/main/java/org/greatsql/greatsqlmcp/GreatSqlMcpApplication.java b/src/main/java/org/greatsql/greatsqlmcp/GreatSqlMcpApplication.java deleted file mode 100644 index e758ace..0000000 --- a/src/main/java/org/greatsql/greatsqlmcp/GreatSqlMcpApplication.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.greatsql.greatsqlmcp; - -import org.greatsql.greatsqlmcp.service.DatabaseService; -import org.springframework.ai.tool.ToolCallback; -import org.springframework.ai.tool.ToolCallbacks; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import java.util.List; - -@SpringBootApplication -public class GreatSqlMcpApplication { - - public static void main(String[] args) { - SpringApplication.run(GreatSqlMcpApplication.class, args); - } - - @Bean - public List getToolCallbacks(DatabaseService databaseService) { - return List.of(ToolCallbacks.from(databaseService)); - } -} \ No newline at end of file diff --git a/src/main/java/org/greatsql/greatsqlmcp/controller/McpController.java b/src/main/java/org/greatsql/greatsqlmcp/controller/McpController.java index ea20c48..f256630 100644 --- a/src/main/java/org/greatsql/greatsqlmcp/controller/McpController.java +++ b/src/main/java/org/greatsql/greatsqlmcp/controller/McpController.java @@ -8,6 +8,8 @@ import org.springframework.web.bind.annotation.*; import org.greatsql.greatsqlmcp.config.AuthConfig; import org.springframework.http.HttpStatus; import java.util.Map; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import jakarta.annotation.PostConstruct; @RestController @CrossOrigin(origins = "*") @@ -18,6 +20,11 @@ public class McpController { @Autowired private ObjectMapper objectMapper; + @PostConstruct + public void init() { + objectMapper.registerModule(new JavaTimeModule()); + } + @Autowired private AuthConfig authConfig; @@ -242,6 +249,29 @@ public class McpController { ), "required", new String[]{"database", "tableName", "whereClause"} ) + ), + Map.of( + "name", "createDB", + "description", "创建新数据库", + "inputSchema", Map.of( + "type", "object", + "properties", Map.of( + "databaseName", Map.of( + "type", "string", + "description", "数据库名称" + ) + ), + "required", new String[]{"databaseName"} + ) + ), + Map.of( + "name", "checkCriticalTransactions", + "description", "检查当前是否有活跃的大事务或长事务", + "inputSchema", Map.of( + "type", "object", + "properties", Map.of(), + "required", new String[]{} + ) ) } ); @@ -315,6 +345,16 @@ public class McpController { } yield databaseService.deleteData(database, tableName, whereClause); } + case "createDB" -> { + String databaseName = (String) arguments.get("databaseName"); + if (databaseName == null) { + yield Map.of("error", "数据库名称不能为空"); + } + yield databaseService.createDB(databaseName); + } + case "checkCriticalTransactions" -> { + yield databaseService.checkCriticalTransactions(); + } default -> Map.of("error", "未知的工具: " + name); }; return Map.of( @@ -327,4 +367,4 @@ public class McpController { ); } -} \ No newline at end of file +} diff --git a/src/main/java/org/greatsql/greatsqlmcp/service/DatabaseService.java b/src/main/java/org/greatsql/greatsqlmcp/service/DatabaseService.java index 0e212be..3af8fd1 100644 --- a/src/main/java/org/greatsql/greatsqlmcp/service/DatabaseService.java +++ b/src/main/java/org/greatsql/greatsqlmcp/service/DatabaseService.java @@ -273,4 +273,50 @@ public class DatabaseService { } } -} \ No newline at end of file + @Tool(name = "createDB", description = "创建新数据库") + public boolean createDB( + @ToolParam(description = "数据库名称") String databaseName) { + String sql = "CREATE DATABASE " + databaseName; + + try (Connection conn = connectionService.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.executeUpdate(); + return true; + } catch (SQLException e) { + throw new RuntimeException("创建数据库时出错:" + e.getMessage(), e); + } + } + + @Tool(name = "checkCriticalTransactions", description = "检查当前是否有活跃的大事务或长事务") + public List> checkCriticalTransactions() { + List> results = new ArrayList<>(); + String sql = "SELECT * FROM information_schema.INNODB_TRX WHERE " + + "trx_lock_structs >= 5 OR " + + "trx_rows_locked >= 100 OR " + + "trx_rows_modified >= 100 OR " + + "TIME_TO_SEC(TIMEDIFF(NOW(),trx_started)) > 100"; + + try (Connection conn = connectionService.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql); + ResultSet rs = stmt.executeQuery()) { + + int columnCount = rs.getMetaData().getColumnCount(); + + while (rs.next()) { + Map row = new HashMap<>(); + for (int i = 1; i <= columnCount; i++) { + String columnName = rs.getMetaData().getColumnName(i); + Object value = rs.getObject(i); + row.put(columnName, value); + } + results.add(row); + } + } catch (SQLException e) { + throw new RuntimeException("查询需要关注的事务时出错:" + e.getMessage(), e); + } + + return results; + } + +} -- Gitee From 95c53ea265d2148cc6bcfb63318c40d7f66e0b14 Mon Sep 17 00:00:00 2001 From: GreatSQL Date: Fri, 22 Aug 2025 10:52:38 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=96=B0=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=EF=BC=9A=E6=9F=A5=E7=9C=8BSQL=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E5=B9=B3=E5=9D=87=E5=93=8D=E5=BA=94=E8=80=97=E6=97=B6=E5=87=BD?= =?UTF-8?q?=E6=95=B0avgSQLRT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../greatsqlmcp/controller/McpController.java | 10 +++++++ .../greatsqlmcp/service/DatabaseService.java | 30 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/main/java/org/greatsql/greatsqlmcp/controller/McpController.java b/src/main/java/org/greatsql/greatsqlmcp/controller/McpController.java index f256630..5d7bcdb 100644 --- a/src/main/java/org/greatsql/greatsqlmcp/controller/McpController.java +++ b/src/main/java/org/greatsql/greatsqlmcp/controller/McpController.java @@ -272,6 +272,15 @@ public class McpController { "properties", Map.of(), "required", new String[]{} ) + ), + Map.of( + "name", "avgSQLRT", + "description", "计算SQL请求平均响应耗时", + "inputSchema", Map.of( + "type", "object", + "properties", Map.of(), + "required", new String[]{} + ) ) } ); @@ -293,6 +302,7 @@ public class McpController { Object result = switch (name) { case "listDatabases" -> databaseService.listDatabases(); + case "avgSQLRT" -> databaseService.avgSQLRT(); case "listTables" -> { String database = (String) arguments.get("database"); if (database == null) { diff --git a/src/main/java/org/greatsql/greatsqlmcp/service/DatabaseService.java b/src/main/java/org/greatsql/greatsqlmcp/service/DatabaseService.java index 3af8fd1..bbfe5a8 100644 --- a/src/main/java/org/greatsql/greatsqlmcp/service/DatabaseService.java +++ b/src/main/java/org/greatsql/greatsqlmcp/service/DatabaseService.java @@ -319,4 +319,34 @@ public class DatabaseService { return results; } + @Tool(name = "avgSQLRT", description = "计算SQL请求平均响应耗时") + public double avgSQLRT() { + String sql = "SELECT BENCHMARK(1000000,AES_ENCRYPT('hello','GreatSQL'))"; + long totalTime = 0; + int iterations = 10; + + try (Connection conn = connectionService.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + for (int i = 0; i < iterations; i++) { + long startTime = System.currentTimeMillis(); + stmt.executeQuery(); + long endTime = System.currentTimeMillis(); + totalTime += (endTime - startTime); + Thread.sleep(1000); // 间隔1秒 + } + + double avgTime = (double) totalTime / iterations; + + if (avgTime > 50) { + System.out.println("严重级告警:SQL请求平均响应耗时 " + avgTime + " ms"); + } else if (avgTime > 10) { + System.out.println("一般级告警:SQL请求平均响应耗时 " + avgTime + " ms"); + } + + return avgTime; + } catch (SQLException | InterruptedException e) { + throw new RuntimeException("计算SQL请求平均响应耗时失败:" + e.getMessage(), e); + } + } } -- Gitee From 69200dfbed51390cd733a00a55d839a587c3d2a9 Mon Sep 17 00:00:00 2001 From: GreatSQL Date: Mon, 25 Aug 2025 10:41:42 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=87=BD=E6=95=B0?= =?UTF-8?q?=EF=BC=9AlistNotableWaitEvents=EF=BC=8C=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=E9=9C=80=E8=A6=81=E5=85=B3=E6=B3=A8=E7=9A=84=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E7=AD=89=E5=BE=85=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../greatsqlmcp/controller/McpController.java | 12 +++ .../greatsqlmcp/service/DatabaseService.java | 88 +++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/src/main/java/org/greatsql/greatsqlmcp/controller/McpController.java b/src/main/java/org/greatsql/greatsqlmcp/controller/McpController.java index 5d7bcdb..b6d2620 100644 --- a/src/main/java/org/greatsql/greatsqlmcp/controller/McpController.java +++ b/src/main/java/org/greatsql/greatsqlmcp/controller/McpController.java @@ -281,6 +281,15 @@ public class McpController { "properties", Map.of(), "required", new String[]{} ) + ), + Map.of( + "name", "listNotableWaitEvents", + "description", "检查需要关注的数据库等待事件", + "inputSchema", Map.of( + "type", "object", + "properties", Map.of(), + "required", new String[]{} + ) ) } ); @@ -365,6 +374,9 @@ public class McpController { case "checkCriticalTransactions" -> { yield databaseService.checkCriticalTransactions(); } + case "listNotableWaitEvents" -> { + yield databaseService.listNotableWaitEvents(); + } default -> Map.of("error", "未知的工具: " + name); }; return Map.of( diff --git a/src/main/java/org/greatsql/greatsqlmcp/service/DatabaseService.java b/src/main/java/org/greatsql/greatsqlmcp/service/DatabaseService.java index bbfe5a8..36ab463 100644 --- a/src/main/java/org/greatsql/greatsqlmcp/service/DatabaseService.java +++ b/src/main/java/org/greatsql/greatsqlmcp/service/DatabaseService.java @@ -349,4 +349,92 @@ public class DatabaseService { throw new RuntimeException("计算SQL请求平均响应耗时失败:" + e.getMessage(), e); } } + + @Tool(name = "listNotableWaitEvents", description = "检查需要关注的数据库等待事件") + public Map listNotableWaitEvents() { + Map results = new HashMap<>(); + + try (Connection conn = connectionService.getConnection()) { + // 1. 检查行锁等待 + checkRowLockWaits(conn, results); + + // 2. 检查Buffer Pool等待 + checkBufferPoolWaits(conn, results); + + // 3. 检查Redo Log等待 + checkRedoLogWaits(conn, results); + + // 4. 检查Undo Log清理 + checkUndoLogPurge(conn, results); + + } catch (SQLException e) { + throw new RuntimeException("检查等待事件时出错:" + e.getMessage(), e); + } + + return results; + } + + private void checkRowLockWaits(Connection conn, Map results) throws SQLException { + String sql = "SELECT variable_value FROM performance_schema.global_status " + + "WHERE variable_name = 'Innodb_row_lock_current_waits'"; + try (PreparedStatement stmt = conn.prepareStatement(sql); + ResultSet rs = stmt.executeQuery()) { + if (rs.next()) { + int value = rs.getInt(1); + if (value > 10) { + results.put("row_lock_wait", "严重级告警:当前有 " + value + " 个活跃的行锁等待,请DBA立即介入检查"); + } else if (value > 0) { + results.put("row_lock_wait", "一般级告警:当前有 " + value + " 个活跃的行锁等待,建议DBA检查"); + } + } + } + } + + private void checkBufferPoolWaits(Connection conn, Map results) throws SQLException { + String sql = "SELECT variable_value FROM performance_schema.global_status " + + "WHERE variable_name = 'Innodb_buffer_pool_wait_free'"; + try (PreparedStatement stmt = conn.prepareStatement(sql); + ResultSet rs = stmt.executeQuery()) { + if (rs.next()) { + int value = rs.getInt(1); + if (value > 10) { + results.put("buffer_pool_wait", "严重级告警:Buffer Pool等待事件 " + value + " 次,请立即调大innodb_buffer_pool_size并检查"); + } else if (value > 0) { + results.put("buffer_pool_wait", "一般级告警:Buffer Pool等待事件 " + value + " 次,建议调大innodb_buffer_pool_size"); + } + } + } + } + + private void checkRedoLogWaits(Connection conn, Map results) throws SQLException { + String sql = "SELECT variable_value FROM performance_schema.global_status " + + "WHERE variable_name = 'Innodb_log_waits'"; + try (PreparedStatement stmt = conn.prepareStatement(sql); + ResultSet rs = stmt.executeQuery()) { + if (rs.next()) { + int value = rs.getInt(1); + if (value > 10) { + results.put("redo_log_wait", "严重级告警:Redo Log等待事件 " + value + " 次,请立即调大innodb_log_buffer_size并检查"); + } else if (value > 0) { + results.put("redo_log_wait", "一般级告警:Redo Log等待事件 " + value + " 次,建议调大innodb_log_buffer_size"); + } + } + } + } + + private void checkUndoLogPurge(Connection conn, Map results) throws SQLException { + String sql = "SELECT COUNT, COMMENT FROM information_schema.INNODB_METRICS " + + "WHERE NAME = 'trx_rseg_history_len'"; + try (PreparedStatement stmt = conn.prepareStatement(sql); + ResultSet rs = stmt.executeQuery()) { + if (rs.next()) { + int value = rs.getInt(1); + if (value > 5000) { + results.put("undo_log_purge", "严重级告警:未清理的undo log数量 " + value + ",请DBA立即介入检查"); + } else if (value > 1000) { + results.put("undo_log_purge", "一般级告警:未清理的undo log数量 " + value + ",建议DBA检查"); + } + } + } + } } -- Gitee From e201761b206ae2c3c16481b2b3cd1ace6ce00806 Mon Sep 17 00:00:00 2001 From: GreatSQL Date: Mon, 25 Aug 2025 11:10:27 +0800 Subject: [PATCH 5/7] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=87=BD=E6=95=B0?= =?UTF-8?q?=EF=BC=9AcheckMGRStatus=EF=BC=8C=E4=BD=9C=E7=94=A8=EF=BC=9A?= =?UTF-8?q?=E7=9B=91=E6=8E=A7MGR=E9=9B=86=E7=BE=A4=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../greatsqlmcp/controller/McpController.java | 12 ++++ .../greatsqlmcp/service/DatabaseService.java | 68 +++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/src/main/java/org/greatsql/greatsqlmcp/controller/McpController.java b/src/main/java/org/greatsql/greatsqlmcp/controller/McpController.java index b6d2620..ac8f4c8 100644 --- a/src/main/java/org/greatsql/greatsqlmcp/controller/McpController.java +++ b/src/main/java/org/greatsql/greatsqlmcp/controller/McpController.java @@ -290,6 +290,15 @@ public class McpController { "properties", Map.of(), "required", new String[]{} ) + ), + Map.of( + "name", "checkMGRStatus", + "description", "监控MGR集群状态", + "inputSchema", Map.of( + "type", "object", + "properties", Map.of(), + "required", new String[]{} + ) ) } ); @@ -377,6 +386,9 @@ public class McpController { case "listNotableWaitEvents" -> { yield databaseService.listNotableWaitEvents(); } + case "checkMGRStatus" -> { + yield databaseService.checkMGRStatus(); + } default -> Map.of("error", "未知的工具: " + name); }; return Map.of( diff --git a/src/main/java/org/greatsql/greatsqlmcp/service/DatabaseService.java b/src/main/java/org/greatsql/greatsqlmcp/service/DatabaseService.java index 36ab463..e779c8e 100644 --- a/src/main/java/org/greatsql/greatsqlmcp/service/DatabaseService.java +++ b/src/main/java/org/greatsql/greatsqlmcp/service/DatabaseService.java @@ -437,4 +437,72 @@ public class DatabaseService { } } } + + @Tool(name = "checkMGRStatus", description = "监控MGR集群状态") + public Map checkMGRStatus() { + Map results = new HashMap<>(); + + try (Connection conn = connectionService.getConnection()) { + // 1. 检查MGR是否已启用 + checkMGREnabled(conn, results); + + // 2. 检查MGR事务队列状态 + checkMGRTransactionQueue(conn, results); + + } catch (SQLException e) { + throw new RuntimeException("检查MGR状态时出错:" + e.getMessage(), e); + } + + return results; + } + + private void checkMGREnabled(Connection conn, Map results) throws SQLException { + String sql = "SELECT * FROM performance_schema.replication_group_members"; + try (PreparedStatement stmt = conn.prepareStatement(sql); + ResultSet rs = stmt.executeQuery()) { + if (!rs.next()) { + results.put("mgr_enabled", "当前没有启用MGR"); + return; + } + + boolean hasOnlineMember = false; + do { + if ("ONLINE".equals(rs.getString("MEMBER_STATE"))) { + hasOnlineMember = true; + break; + } + } while (rs.next()); + + if (!hasOnlineMember) { + results.put("mgr_status", "严重级告警:MGR已启用但无ONLINE状态的成员"); + } else { + results.put("mgr_status", "MGR运行正常"); + } + } + } + + private void checkMGRTransactionQueue(Connection conn, Map results) throws SQLException { + String sql = "SELECT MEMBER_ID as id, COUNT_TRANSACTIONS_IN_QUEUE as trx_tobe_certified, " + + "COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE as relaylog_tobe_applied " + + "FROM performance_schema.replication_group_member_stats"; + try (PreparedStatement stmt = conn.prepareStatement(sql); + ResultSet rs = stmt.executeQuery()) { + if (rs.next()) { + int trxToCertify = rs.getInt("trx_tobe_certified"); + int relaylogToApply = rs.getInt("relaylog_tobe_applied"); + + if (trxToCertify > 100) { + results.put("mgr_trx_certify", "严重级告警:待认证事务队列大小 " + trxToCertify); + } else if (trxToCertify > 10) { + results.put("mgr_trx_certify", "一般级关注:待认证事务队列大小 " + trxToCertify); + } + + if (relaylogToApply > 100) { + results.put("mgr_relaylog_apply", "严重级告警:待回放事务队列大小 " + relaylogToApply); + } else if (relaylogToApply > 10) { + results.put("mgr_relaylog_apply", "一般级关注:待回放事务队列大小 " + relaylogToApply); + } + } + } + } } -- Gitee From 89f6a8afef5958bb19155e075921497e4c7a48fe Mon Sep 17 00:00:00 2001 From: GreatSQL Date: Mon, 25 Aug 2025 16:17:07 +0800 Subject: [PATCH 6/7] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=87=BD=E6=95=B0?= =?UTF-8?q?=EF=BC=9AfindPotentialMemoryHogs=EF=BC=8C=E4=BD=9C=E7=94=A8?= =?UTF-8?q?=EF=BC=9A=E6=A3=80=E6=9F=A5=E6=95=B0=E6=8D=AE=E5=BA=93=E4=B8=AD?= =?UTF-8?q?=E6=98=AF=E5=90=A6=E5=AD=98=E5=9C=A8=E5=86=85=E5=AD=98=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../greatsqlmcp/controller/McpController.java | 12 +++ .../greatsqlmcp/service/DatabaseService.java | 82 +++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/src/main/java/org/greatsql/greatsqlmcp/controller/McpController.java b/src/main/java/org/greatsql/greatsqlmcp/controller/McpController.java index ac8f4c8..d0c03ca 100644 --- a/src/main/java/org/greatsql/greatsqlmcp/controller/McpController.java +++ b/src/main/java/org/greatsql/greatsqlmcp/controller/McpController.java @@ -299,6 +299,15 @@ public class McpController { "properties", Map.of(), "required", new String[]{} ) + ), + Map.of( + "name", "findPotentialMemoryHogs", + "description", "检查数据库中是否存在内存异常情况", + "inputSchema", Map.of( + "type", "object", + "properties", Map.of(), + "required", new String[]{} + ) ) } ); @@ -389,6 +398,9 @@ public class McpController { case "checkMGRStatus" -> { yield databaseService.checkMGRStatus(); } + case "findPotentialMemoryHogs" -> { + yield databaseService.findPotentialMemoryHogs(); + } default -> Map.of("error", "未知的工具: " + name); }; return Map.of( diff --git a/src/main/java/org/greatsql/greatsqlmcp/service/DatabaseService.java b/src/main/java/org/greatsql/greatsqlmcp/service/DatabaseService.java index e779c8e..8b4bd8d 100644 --- a/src/main/java/org/greatsql/greatsqlmcp/service/DatabaseService.java +++ b/src/main/java/org/greatsql/greatsqlmcp/service/DatabaseService.java @@ -505,4 +505,86 @@ public class DatabaseService { } } } + + @Tool(name = "findPotentialMemoryHogs", description = "检查数据库中是否存在内存异常情况") + public Map findPotentialMemoryHogs() { + Map results = new HashMap<>(); + + try (Connection conn = connectionService.getConnection()) { + // 1. 检查全局内存模块异常 + checkGlobalMemoryEvents(conn, results); + + // 2. 检查线程内存异常 + checkThreadMemoryEvents(conn, results); + + } catch (SQLException e) { + throw new RuntimeException("检查内存异常时出错:" + e.getMessage(), e); + } + + return results; + } + + private void checkGlobalMemoryEvents(Connection conn, Map results) throws SQLException { + String sql = "SELECT EVENT_NAME, SUM_NUMBER_OF_BYTES_ALLOC FROM " + + "PERFORMANCE_SCHEMA.MEMORY_SUMMARY_GLOBAL_BY_EVENT_NAME " + + "WHERE SUM_NUMBER_OF_BYTES_ALLOC >= 1073741824 " + + "ORDER BY SUM_NUMBER_OF_BYTES_ALLOC DESC"; + + try (PreparedStatement stmt = conn.prepareStatement(sql); + ResultSet rs = stmt.executeQuery()) { + + while (rs.next()) { + String eventName = rs.getString("EVENT_NAME"); + long bytesAlloc = rs.getLong("SUM_NUMBER_OF_BYTES_ALLOC"); + + if ("memory/innodb/buf_buf_pool".equals(eventName)) { + // 检查innodb buffer pool是否异常溢出 + long bufferPoolSize = getInnoDBBufferPoolSize(conn); + if (bytesAlloc > bufferPoolSize * 1.2) { + results.put("memory_innodb_buffer_pool", "严重级告警:InnoDB Buffer Pool内存使用量(" + bytesAlloc + " bytes)超过配置值(" + bufferPoolSize + " bytes),可能存在内存泄漏风险"); + } + } else if (eventName.startsWith("memory/sql/") || "memory/memory/HP_PTRS".equals(eventName) || "memory/sql/Filesort_buffer::sort_keys".equals(eventName)) { + results.put("memory_inefficient_sql", "一般级告警:模块 " + eventName + " 内存使用量较高(" + bytesAlloc + " bytes),可能存在低效SQL,建议检查慢查询并优化"); + } + } + } + } + + private long getInnoDBBufferPoolSize(Connection conn) throws SQLException { + String sql = "SHOW VARIABLES LIKE 'innodb_buffer_pool_size'"; + try (PreparedStatement stmt = conn.prepareStatement(sql); + ResultSet rs = stmt.executeQuery()) { + if (rs.next()) { + return rs.getLong("Value"); + } + } + return 0; + } + + private void checkThreadMemoryEvents(Connection conn, Map results) throws SQLException { + String sql = "SELECT THREAD_ID, EVENT_NAME, SUM_NUMBER_OF_BYTES_ALLOC FROM " + + "PERFORMANCE_SCHEMA.MEMORY_SUMMARY_BY_THREAD_BY_EVENT_NAME " + + "WHERE SUM_NUMBER_OF_BYTES_ALLOC >= 1073741824 " + + "ORDER BY SUM_NUMBER_OF_BYTES_ALLOC DESC"; + + try (PreparedStatement stmt = conn.prepareStatement(sql); + ResultSet rs = stmt.executeQuery()) { + + int highMemoryThreads = 0; + while (rs.next()) { + String eventName = rs.getString("EVENT_NAME"); + long bytesAlloc = rs.getLong("SUM_NUMBER_OF_BYTES_ALLOC"); + + if (eventName.startsWith("memory/innodb/") || eventName.startsWith("memory/sql/")) { + highMemoryThreads++; + } + } + + if (highMemoryThreads > 10) { + results.put("memory_high_threads", "严重级告警:当前有 " + highMemoryThreads + " 个线程内存使用量超过1GB,可能存在大量活跃连接或低效SQL,建议检查慢查询并优化"); + } else if (highMemoryThreads > 0) { + results.put("memory_high_threads", "一般级告警:当前有 " + highMemoryThreads + " 个线程内存使用量超过1GB,建议检查慢查询并优化"); + } + } + } } -- Gitee From 51956e3f18aa420e054673856094bb6b5a319199 Mon Sep 17 00:00:00 2001 From: YeJinrong Date: Mon, 25 Aug 2025 16:24:23 +0800 Subject: [PATCH 7/7] =?UTF-8?q?=E5=87=BD=E6=95=B0findPotentialMemoryHogs?= =?UTF-8?q?=E6=94=B9=E5=90=8D=E4=B8=BAfindAbnormalMemoryIssue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/greatsql/greatsqlmcp/controller/McpController.java | 6 +++--- .../org/greatsql/greatsqlmcp/service/DatabaseService.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/greatsql/greatsqlmcp/controller/McpController.java b/src/main/java/org/greatsql/greatsqlmcp/controller/McpController.java index d0c03ca..1de0e90 100644 --- a/src/main/java/org/greatsql/greatsqlmcp/controller/McpController.java +++ b/src/main/java/org/greatsql/greatsqlmcp/controller/McpController.java @@ -301,7 +301,7 @@ public class McpController { ) ), Map.of( - "name", "findPotentialMemoryHogs", + "name", "findAbnormalMemoryIssue", "description", "检查数据库中是否存在内存异常情况", "inputSchema", Map.of( "type", "object", @@ -398,8 +398,8 @@ public class McpController { case "checkMGRStatus" -> { yield databaseService.checkMGRStatus(); } - case "findPotentialMemoryHogs" -> { - yield databaseService.findPotentialMemoryHogs(); + case "findAbnormalMemoryIssue" -> { + yield databaseService.findAbnormalMemoryIssue(); } default -> Map.of("error", "未知的工具: " + name); }; diff --git a/src/main/java/org/greatsql/greatsqlmcp/service/DatabaseService.java b/src/main/java/org/greatsql/greatsqlmcp/service/DatabaseService.java index 8b4bd8d..f35195c 100644 --- a/src/main/java/org/greatsql/greatsqlmcp/service/DatabaseService.java +++ b/src/main/java/org/greatsql/greatsqlmcp/service/DatabaseService.java @@ -506,8 +506,8 @@ public class DatabaseService { } } - @Tool(name = "findPotentialMemoryHogs", description = "检查数据库中是否存在内存异常情况") - public Map findPotentialMemoryHogs() { + @Tool(name = "findAbnormalMemoryIssue", description = "检查数据库中是否存在内存异常情况") + public Map findAbnormalMemoryIssue() { Map results = new HashMap<>(); try (Connection conn = connectionService.getConnection()) { -- Gitee