From e7576af4304d4ee884d251e5d99d36afcb6bc232 Mon Sep 17 00:00:00 2001 From: dazer007 Date: Wed, 22 Jan 2025 16:16:02 +0800 Subject: [PATCH] feat(all): add killDeadLockSession --- ruoyi-admin/pom.xml | 2 +- .../com/neuhis/his/domain/dto/DeadLock.java | 30 +--- .../neuhis/his/domain/dto/DeadLockRac.java | 58 +++++++ .../neuhis/his/mapper/OracleSysMapper.java | 26 ++- .../neuhis/his/service/IOracleSysService.java | 14 +- .../service/impl/OracleSysServiceImpl.java | 92 +++++++++- .../java/com/neuhis/quartz/task/HisTask.java | 24 ++- .../src/main/resources/application-druid.yml | 61 ------- .../main/resources/application-xyzxhis.yml | 41 +---- .../resources/mapper/his/OracleSysMapper.xml | 157 +++++++++++++++++- .../ruoyi/common/enums/DataSourceType.java | 5 +- ...240601.sql => ry_neuhisv0611-20250122.sql} | 0 start_NeuHis_dev.sh | 21 +++ stop_NeuHis_dev.sh | 10 ++ 14 files changed, 406 insertions(+), 135 deletions(-) create mode 100644 ruoyi-admin/src/main/java/com/neuhis/his/domain/dto/DeadLockRac.java delete mode 100644 ruoyi-admin/src/main/resources/application-druid.yml rename sql/{ry_20240601.sql => ry_neuhisv0611-20250122.sql} (100%) create mode 100644 start_NeuHis_dev.sh create mode 100644 stop_NeuHis_dev.sh diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml index cfd6010a5..29d594b95 100644 --- a/ruoyi-admin/pom.xml +++ b/ruoyi-admin/pom.xml @@ -9,7 +9,7 @@ 4.0.0 jar - ruoyi-admin + ruoyi-admin-neuhisutools web服务入口 diff --git a/ruoyi-admin/src/main/java/com/neuhis/his/domain/dto/DeadLock.java b/ruoyi-admin/src/main/java/com/neuhis/his/domain/dto/DeadLock.java index 6c9d9c0da..eb4ec2141 100644 --- a/ruoyi-admin/src/main/java/com/neuhis/his/domain/dto/DeadLock.java +++ b/ruoyi-admin/src/main/java/com/neuhis/his/domain/dto/DeadLock.java @@ -1,32 +1,12 @@ package com.neuhis.his.domain.dto; -public class DeadLock { +import lombok.Getter; +import lombok.Setter; +@Getter +@Setter +public class DeadLock { private String sid; private String serial; private String sessionStr; - - public String getSid() { - return sid; - } - - public void setSid(String sid) { - this.sid = sid; - } - - public String getSerial() { - return serial; - } - - public void setSerial(String serial) { - this.serial = serial; - } - - public String getSessionStr() { - return sessionStr; - } - - public void setSessionStr(String sessionStr) { - this.sessionStr = sessionStr; - } } diff --git a/ruoyi-admin/src/main/java/com/neuhis/his/domain/dto/DeadLockRac.java b/ruoyi-admin/src/main/java/com/neuhis/his/domain/dto/DeadLockRac.java new file mode 100644 index 000000000..1010745d9 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/neuhis/his/domain/dto/DeadLockRac.java @@ -0,0 +1,58 @@ +package com.neuhis.his.domain.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class DeadLockRac { + private String 数据库服务器; + private String 登录用户名; + private String 锁的对象; + /** + * 进程id + */ + private Integer 本进程号SID; + /** + * 情况1:(SID:2009)会话 SQL已跑完 ==>忽略 + * 情况2:(SID:4017)会话 正执行SQL:delete xx ==>忽略 + * 情况3:根锁为此会话杀我KILL ME or igore==> 杀我,或者忽略 + * 情况4:<根锁会话>4903 【SID=4903】的SQL阻塞了本语句[5728]杀掉他==> 杀掉这个:【SID】 + */ + private String 阻塞SID; + private String STATUS; + private String SQL_TEXT; + private String CLIENT_INFO; + private String program; + private String REQUEST; + private String STATE; + private String EVENT; + /** + * 电脑ip + */ + private String MACHINE; + private String LOGON_TIME; + private String INST_ID; + /** + * 等待时间 + */ + private String 等待时间; + private String BLOCKING_SESSION; + private String PROGRAM; + private String sessionStr; + private String norac_KILL; + /** + * 杀锁SQL,形如: + * ALTER SYSTEM KILL SESSION '6439,1899,@2' immediate ; + */ + private String rac_KILL; + + public DeadLockRac() { + } + + public DeadLockRac(Integer 本进程号SID, String 阻塞SID) { + this.本进程号SID = 本进程号SID; + this.阻塞SID = 阻塞SID; + } + +} diff --git a/ruoyi-admin/src/main/java/com/neuhis/his/mapper/OracleSysMapper.java b/ruoyi-admin/src/main/java/com/neuhis/his/mapper/OracleSysMapper.java index 7e1b9fac7..4048a62be 100644 --- a/ruoyi-admin/src/main/java/com/neuhis/his/mapper/OracleSysMapper.java +++ b/ruoyi-admin/src/main/java/com/neuhis/his/mapper/OracleSysMapper.java @@ -1,14 +1,36 @@ package com.neuhis.his.mapper; import com.neuhis.his.domain.dto.DeadLock; -import com.neuhis.his.domain.entity.MetMrsBase; +import com.neuhis.his.domain.dto.DeadLockRac; import java.util.List; public interface OracleSysMapper { - List getDeadLockSession(); + /** + * 简版: 获取死锁进程 + * 主要用途是识别出那些已经处于等待锁状态超过5分钟并且当前是活跃的数据库会话, + * 这对于诊断和解决数据库死锁或长时间运行的事务问题非常有用 + */ + List getDeadLockSessionV1(); + /** + * RAC方式,获取死锁对象 + * --查询跟踪卡顿的阻塞锁会话进程SQL(集群RAC),速度很快 + * --解释:从 RAC(Real Application Clusters)环境中获取当前存在锁争用(blocking locks)的会话信息。查询结果提供了关于阻塞会话的各种细节,包括锁定的对象、会话状态、等待时间和可能的解锁命令等 + * --查杀步骤:查看【等待时间】最长,【RAC_KILL】 != 的进行 杀进程 + * --查询跟踪卡顿的阻塞锁会话进程SQL + */ + List getDeadLockSessionV2Rac(); + /** + * Slow版本 + */ + List getDeadLockSessionV3Slow(); + + /** + * 形如:ALTER SYSTEM KILL SESSION '4150,14295,@1' immediate ; + * @param sessionStr '4150,14295,@1' + */ void killSession(String sessionStr); } \ No newline at end of file diff --git a/ruoyi-admin/src/main/java/com/neuhis/his/service/IOracleSysService.java b/ruoyi-admin/src/main/java/com/neuhis/his/service/IOracleSysService.java index 9b8b08f0b..63b5e4c9b 100644 --- a/ruoyi-admin/src/main/java/com/neuhis/his/service/IOracleSysService.java +++ b/ruoyi-admin/src/main/java/com/neuhis/his/service/IOracleSysService.java @@ -1,12 +1,24 @@ package com.neuhis.his.service; import com.neuhis.his.domain.dto.DeadLock; +import com.neuhis.his.domain.dto.DeadLockRac; import java.util.List; public interface IOracleSysService { - List getDeadLockSession(); + /** + * 简易版查询,速度很快 + */ + List getDeadLockSessionV1(); + /** + * RAC版本,速度很快 + */ + List getDeadLockSessionV2Rac(); + /** + * slow版本 + */ + List getDeadLockSessionV3Slow(); void killSession(String sessionStr); } diff --git a/ruoyi-admin/src/main/java/com/neuhis/his/service/impl/OracleSysServiceImpl.java b/ruoyi-admin/src/main/java/com/neuhis/his/service/impl/OracleSysServiceImpl.java index c7416525d..58ca2a12e 100644 --- a/ruoyi-admin/src/main/java/com/neuhis/his/service/impl/OracleSysServiceImpl.java +++ b/ruoyi-admin/src/main/java/com/neuhis/his/service/impl/OracleSysServiceImpl.java @@ -1,16 +1,23 @@ package com.neuhis.his.service.impl; +import cn.hutool.core.collection.ListUtil; +import com.neuhis.his.domain.dto.DeadLockRac; import com.ruoyi.common.annotation.DataSource; import com.ruoyi.common.enums.DataSourceType; import com.neuhis.his.domain.dto.DeadLock; -import com.neuhis.his.mapper.MetMrsBaseMapper; import com.neuhis.his.mapper.OracleSysMapper; import com.neuhis.his.service.IOracleSysService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.compress.utils.Lists; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import javax.annotation.Resource; +import java.util.HashMap; import java.util.List; +import java.util.Map; +@Slf4j @Service @DataSource(value = DataSourceType.SLAVE) public class OracleSysServiceImpl implements IOracleSysService { @@ -19,16 +26,93 @@ public class OracleSysServiceImpl implements IOracleSysService { private OracleSysMapper oracleSysMapper; @Override - public List getDeadLockSession() { - return oracleSysMapper.getDeadLockSession(); + public List getDeadLockSessionV1() { + return oracleSysMapper.getDeadLockSessionV1(); + } + + @Override + public List getDeadLockSessionV2Rac() { + List deadLockRacs = oracleSysMapper.getDeadLockSessionV2Rac(); + return this.findV2DeadLockRacSessionId(deadLockRacs); + } + + @Override + public List getDeadLockSessionV3Slow() { + return oracleSysMapper.getDeadLockSessionV3Slow(); } @Override public void killSession(String sessionStr) { try { - oracleSysMapper.killSession(sessionStr); + if (StringUtils.isNotEmpty(sessionStr)) { + log.info("查杀进程id:" + sessionStr); + oracleSysMapper.killSession(sessionStr); + } }catch (Exception e){ System.out.println("数据库啥死锁异常:"+e.getMessage()); } } + + /** + * 从进程列表找到,死锁的进程 + */ + private List findV2DeadLockRacSessionId(List deadLockRacs) { + //找到有问题的进程 + DeadLockRac problematicSession = DeadlockResolver.findProblematicSession(deadLockRacs); + if (problematicSession != null) { + return ListUtil.of(problematicSession); + } + return Lists.newArrayList(); + } + + //进程列表:本进程号SID、阻塞id 三列 + //1、“阻塞id”有四种情况,先找:“的SQL阻塞了本语句”的取得:“【SID=”的取值 + //2、找到:“本进程号SID” = “【SID=”的一行,如果本行:“阻塞id”是 情况3,结束,否则继续递归。 + private static class DeadlockResolver { + public static final String BLOCK_FIND_TXT = "的SQL阻塞了本语句"; + public static final String BLOCK_ROOT_TXT = "根锁为此会话杀我KILL ME or igore"; + public static DeadLockRac findProblematicSession(List deadlockList) { + Map sidMap = new HashMap<>(); + + // First pass: build a map for quick lookup by SID + for (DeadLockRac deadlock : deadlockList) { + sidMap.put(deadlock.get本进程号SID(), deadlock); + } + + // Second pass: look for entries that match the criteria + for (DeadLockRac deadlock : deadlockList) { + if (deadlock.get阻塞SID().contains(BLOCK_FIND_TXT)) { + // Extract the blocking SID + int blockingSID = extractBlockingSID(deadlock.get阻塞SID()); + DeadLockRac blockingSession = sidMap.get(blockingSID); + + // Check if the blocking session's 阻塞SID is of case 3 + while (blockingSession != null && !isRootBlock(blockingSession.get阻塞SID())) { + // If not case 3, recursively check the blocker of this session + String sqlText = blockingSession.get阻塞SID(); + if (sqlText.contains(BLOCK_FIND_TXT)) { + blockingSID = extractBlockingSID(sqlText); + blockingSession = sidMap.get(blockingSID); + } else { + break; // Exit loop if it doesn't match case 4 anymore + } + } + if (blockingSession != null && isRootBlock(blockingSession.get阻塞SID())) { + return blockingSession; // Found problematic session + } + } + } + return null; // No problematic session found + } + + private static boolean isRootBlock(String sqlText) { + return sqlText.contains(BLOCK_ROOT_TXT); + } + + private static int extractBlockingSID(String sqlText) { + // Assuming format is like "...【SID=4903】..." + String sidStr = sqlText.substring(sqlText.indexOf("【SID=") + 5, sqlText.indexOf("】")); + return Integer.parseInt(sidStr.trim()); + } + } } diff --git a/ruoyi-admin/src/main/java/com/neuhis/quartz/task/HisTask.java b/ruoyi-admin/src/main/java/com/neuhis/quartz/task/HisTask.java index ac9be09c7..4ec79991b 100644 --- a/ruoyi-admin/src/main/java/com/neuhis/quartz/task/HisTask.java +++ b/ruoyi-admin/src/main/java/com/neuhis/quartz/task/HisTask.java @@ -1,7 +1,10 @@ package com.neuhis.quartz.task; import cn.hutool.core.date.DateUtil; +import com.neuhis.his.domain.dto.DeadLock; +import com.neuhis.his.domain.dto.DeadLockRac; import com.neuhis.his.push.OracleSlaveDataAutoPushService; +import com.neuhis.his.service.IOracleSysService; import com.neuhis.quartz.task.common.JobService; import com.ruoyi.common.config.RuoYiConfig; import com.neuhis.quartz.task.common.JobSwitchConstant; @@ -9,6 +12,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import java.util.List; + /** * 定时任务调度 * @@ -20,9 +25,10 @@ public class HisTask { @Autowired JobService jobService; - @Autowired OracleSlaveDataAutoPushService oracleSlaveDataAutoPushService; + @Autowired + private IOracleSysService oracleSysService; public void pushData() { @@ -38,6 +44,7 @@ public class HisTask { log.info("数据推送结束" + DateUtil.format(DateUtil.date(), "yyyy-MM-dd HH:mm:ss")); } } + public void pushDataByCompareId(String apicode) { if (RuoYiConfig.isQuzrtzTask() && jobService.isEnable(JobSwitchConstant.comPatientinfo)) { @@ -47,6 +54,21 @@ public class HisTask { } } + /** + * 杀oracle死锁 + */ + public void killDeadLockSession() { + if (RuoYiConfig.isQuzrtzTask()) { + log.info("杀oracle死锁开始" + DateUtil.format(DateUtil.date(), "yyyy-MM-dd HH:mm:ss")); + ///List deadLocks = oracleSysService.getDeadLockSessionV1(); + ///deadLocks.forEach(deadLock -> HisTask.this.oracleSysService.killSession(deadLock.getSessionStr())); + + List sessionV2Rac = oracleSysService.getDeadLockSessionV2Rac(); + sessionV2Rac.forEach(deadLock -> HisTask.this.oracleSysService.killSession(deadLock.getSessionStr())); + log.info("杀oracle死锁结束" + DateUtil.format(DateUtil.date(), "yyyy-MM-dd HH:mm:ss")); + } + } + /* * 定时删除日志表数据 * */ diff --git a/ruoyi-admin/src/main/resources/application-druid.yml b/ruoyi-admin/src/main/resources/application-druid.yml deleted file mode 100644 index a69d8feb2..000000000 --- a/ruoyi-admin/src/main/resources/application-druid.yml +++ /dev/null @@ -1,61 +0,0 @@ -# 数据源配置 -spring: - datasource: - type: com.alibaba.druid.pool.DruidDataSource - driverClassName: com.mysql.cj.jdbc.Driver - druid: - # 主库数据源 - master: - url: jdbc:mysql://localhost:3306/ry?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 - username: root - password: password - # 从库数据源 - slave: - # 从数据源开关/默认关闭 - enabled: false - url: - username: - password: - # 初始连接数 - initialSize: 5 - # 最小连接池数量 - minIdle: 10 - # 最大连接池数量 - maxActive: 20 - # 配置获取连接等待超时的时间 - maxWait: 60000 - # 配置连接超时时间 - connectTimeout: 30000 - # 配置网络超时时间 - socketTimeout: 60000 - # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 - timeBetweenEvictionRunsMillis: 60000 - # 配置一个连接在池中最小生存的时间,单位是毫秒 - minEvictableIdleTimeMillis: 300000 - # 配置一个连接在池中最大生存的时间,单位是毫秒 - maxEvictableIdleTimeMillis: 900000 - # 配置检测连接是否有效 - validationQuery: SELECT 1 FROM DUAL - testWhileIdle: true - testOnBorrow: false - testOnReturn: false - webStatFilter: - enabled: true - statViewServlet: - enabled: true - # 设置白名单,不填则允许所有访问 - allow: - url-pattern: /druid/* - # 控制台管理用户名和密码 - login-username: ruoyi - login-password: 123456 - filter: - stat: - enabled: true - # 慢SQL记录 - log-slow-sql: true - slow-sql-millis: 1000 - merge-sql: true - wall: - config: - multi-statement-allow: true \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/application-xyzxhis.yml b/ruoyi-admin/src/main/resources/application-xyzxhis.yml index dff909c16..705a03930 100644 --- a/ruoyi-admin/src/main/resources/application-xyzxhis.yml +++ b/ruoyi-admin/src/main/resources/application-xyzxhis.yml @@ -10,7 +10,7 @@ ruoyi: # 版本 version: 1.0.1 # 版权年份 - copyrightYear: 2023 + copyrightYear: 2025 # 实例演示开关 demoEnabled: true # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) @@ -56,8 +56,8 @@ spring: enabled: true driverClassName: oracle.jdbc.OracleDriver url: jdbc:oracle:thin:@20.0.0.40:1521/hisdb - username: crbsb - password: crbsb + username: hit_app + password: hit db1: # 从数据源开关/默认关闭 enabled: true @@ -65,41 +65,6 @@ spring: url: jdbc:oracle:thin:@10.10.10.31:1521/dzbl username: winlis password: lis0928 - db2: - # 从数据源开关/默认关闭 - enabled: false - driverClassName: oracle.jdbc.OracleDriver - url: jdbc:oracle:thin:@10.10.13.10:1521/pacs - username: pacs50 - password: pacs600718 - db3: - # 从数据源开关/默认关闭, - enabled: false - driverClassName: oracle.jdbc.OracleDriver - url: jdbc:oracle:thin:@192.168.1.1:1521/ydsynew - username: crbsb - password: crbsb - db4: - # 从数据源开关/默认关闭 - enabled: false - driverClassName: oracle.jdbc.OracleDriver - url: jdbc:oracle:thin:@192.168.1.1:1521/ydsynew - username: crbsb - password: crbsb - db5: - # 从数据源开关/默认关闭 - enabled: false - driverClassName: oracle.jdbc.OracleDriver - url: jdbc:oracle:thin:@192.168.1.1:1521/ydsynew - username: crbsb - password: crbsb - hitapp: - # 从数据源开关/默认关闭 - enabled: false - driverClassName: oracle.jdbc.OracleDriver - url: jdbc:oracle:thin:@20.0.0.40:1521/hisdb - username: hit_app - password: hit # 初始连接数 initialSize: 5 # 最小连接池数量 diff --git a/ruoyi-admin/src/main/resources/mapper/his/OracleSysMapper.xml b/ruoyi-admin/src/main/resources/mapper/his/OracleSysMapper.xml index 8a695aa65..bece6f8d5 100644 --- a/ruoyi-admin/src/main/resources/mapper/his/OracleSysMapper.xml +++ b/ruoyi-admin/src/main/resources/mapper/his/OracleSysMapper.xml @@ -3,7 +3,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> - SELECT S.SID as sid, s.SERIAL# as serial, @@ -13,6 +13,161 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" and s.lockwait is not null and s.status='ACTIVE' and s.PREV_EXEC_START < sysdate-5/(60*24) + + + + + alter system kill session '${sessionStr}' diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java index 43ed6f438..9bb7003cc 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java @@ -20,5 +20,8 @@ public enum DataSourceType Db1, Db2, Db3, - Db4; + Db4, + Db5, + hitapp + ; } diff --git a/sql/ry_20240601.sql b/sql/ry_neuhisv0611-20250122.sql similarity index 100% rename from sql/ry_20240601.sql rename to sql/ry_neuhisv0611-20250122.sql diff --git a/start_NeuHis_dev.sh b/start_NeuHis_dev.sh new file mode 100644 index 000000000..6b69c5433 --- /dev/null +++ b/start_NeuHis_dev.sh @@ -0,0 +1,21 @@ +#!/bin/bash +APP_NAME=ruoyi-admin-neuhisutools.jar +PID=$(ps -ef|grep $APP_NAME|grep -v grep|awk '{print $2}') +JVM="-server -Xms1g -Xmx4g -Xmn1g -verbose:gc-XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=5 -XX:+ExplicitGCInvokesConcurrent -XX:GCTimeRatio=19 -XX:CMSInitiatingOccupancyFraction=70 -XX:CMSFullGCsBeforeCompaction=0 -Xnoclassgc -XX:SoftRefLRUPolicyMSPerMB=0" + +if [ "$PID" != "" ]; then + echo '================>停止服务..........' + kill -9 $PID + sleep 3s +fi + +echo '================>nohup.out日志清空成功.........' +echo '' > nohup.out +echo '================>开始重启服务.........' + +nohup java $JVM -jar $APP_NAME 2>&1 & +tail -f nohup.out + + + + diff --git a/stop_NeuHis_dev.sh b/stop_NeuHis_dev.sh new file mode 100644 index 000000000..8d68fd6cc --- /dev/null +++ b/stop_NeuHis_dev.sh @@ -0,0 +1,10 @@ +#!/bin/bash +echo '================>停止服务开始..........' +APP_NAME=ruoyi-admin-neuhisutools.jar +PID=$(ps -ef|grep $APP_NAME|grep -v grep|awk '{print $2}') +if [ "$PID" != "" ]; then + echo '================>停止服务结束..........' + kill -9 ${PID} + sleep 3s +fi +