feat(all): add killDeadLockSession

pull/544/head
dazer007 2025-01-22 16:16:02 +08:00
parent 4eee5b3310
commit e7576af430
14 changed files with 406 additions and 135 deletions

View File

@ -9,7 +9,7 @@
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>ruoyi-admin</artifactId>
<artifactId>ruoyi-admin-neuhisutools</artifactId>
<description>
web服务入口

View File

@ -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;
}
}

View File

@ -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) SQLdelete xx ==>
* 3KILL ME or igore==>
* 4<>4903 SID=4903SQL[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;
}
}

View File

@ -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<DeadLock> getDeadLockSession();
/**
* :
* 5,
*
*/
List<DeadLock> getDeadLockSessionV1();
/**
* RAC
* --SQLRAC,
* -- RACReal Application Clustersblocking locks
* --RAC_KILL =
* --SQL
*/
List<DeadLockRac> getDeadLockSessionV2Rac();
/**
* Slow
*/
List<DeadLock> getDeadLockSessionV3Slow();
/**
* ALTER SYSTEM KILL SESSION '4150,14295,@1' immediate ;
* @param sessionStr '4150,14295,@1'
*/
void killSession(String sessionStr);
}

View File

@ -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<DeadLock> getDeadLockSession();
/**
*
*/
List<DeadLock> getDeadLockSessionV1();
/**
* RAC
*/
List<DeadLockRac> getDeadLockSessionV2Rac();
/**
* slow
*/
List<DeadLock> getDeadLockSessionV3Slow();
void killSession(String sessionStr);
}

View File

@ -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<DeadLock> getDeadLockSession() {
return oracleSysMapper.getDeadLockSession();
public List<DeadLock> getDeadLockSessionV1() {
return oracleSysMapper.getDeadLockSessionV1();
}
@Override
public List<DeadLockRac> getDeadLockSessionV2Rac() {
List<DeadLockRac> deadLockRacs = oracleSysMapper.getDeadLockSessionV2Rac();
return this.findV2DeadLockRacSessionId(deadLockRacs);
}
@Override
public List<DeadLock> 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<DeadLockRac> findV2DeadLockRacSessionId(List<DeadLockRac> 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<DeadLockRac> deadlockList) {
Map<Integer, DeadLockRac> sidMap = new HashMap<>();
// First pass: build a map for quick lookup by SID
for (DeadLockRac deadlock : deadlockList) {
sidMap.put(deadlock.getSID(), deadlock);
}
// Second pass: look for entries that match the criteria
for (DeadLockRac deadlock : deadlockList) {
if (deadlock.getSID().contains(BLOCK_FIND_TXT)) {
// Extract the blocking SID
int blockingSID = extractBlockingSID(deadlock.getSID());
DeadLockRac blockingSession = sidMap.get(blockingSID);
// Check if the blocking session's 阻塞SID is of case 3
while (blockingSession != null && !isRootBlock(blockingSession.getSID())) {
// If not case 3, recursively check the blocker of this session
String sqlText = blockingSession.getSID();
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.getSID())) {
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());
}
}
}

View File

@ -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<DeadLock> deadLocks = oracleSysService.getDeadLockSessionV1();
///deadLocks.forEach(deadLock -> HisTask.this.oracleSysService.killSession(deadLock.getSessionStr()));
List<DeadLockRac> 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"));
}
}
/*
*
* */

View File

@ -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

View File

@ -10,7 +10,7 @@ ruoyi:
# 版本
version: 1.0.1
# 版权年份
copyrightYear: 2023
copyrightYear: 2025
# 实例演示开关
demoEnabled: true
# 文件路径 示例( Windows配置D:/ruoyi/uploadPathLinux配置 /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
# 最小连接池数量

View File

@ -3,7 +3,7 @@
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.neuhis.his.mapper.OracleSysMapper">
<select id="getDeadLockSession" resultType="com.neuhis.his.domain.dto.DeadLock">
<select id="getDeadLockSessionV1" resultType="com.neuhis.his.domain.dto.DeadLock">
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 &lt; sysdate-5/(60*24)
</select>
<select id="getDeadLockSessionV2Rac" resultType="com.neuhis.his.domain.dto.DeadLockRac">
<![CDATA[
SELECT DISTINCT decode(s.inst_id, 1, 'DB1', 2, 'DB2' ,s.inst_id) "数据库服务器",
decode(s.BLOCKING_SESSION,'',S.USERNAME,'--'||s.USERNAME) "登陆用户名",
O.OWNER || '.' || O.OBJECT_NAME "锁的对象",
S.SID "本进程号SID",
decode(S.BLOCKING_SESSION,
'',
'根锁为此会话' || '杀我KILL ME or igore',
'<根锁会话>' || S.BLOCKING_SESSION || ' 【SID=' || S.BLOCKING_SESSION || '】的SQL阻塞了本语句 [' || S.SID || '] 杀掉他<根锁会话>' ) "阻塞SID",
S.BLOCKING_SESSION_STATUS "STATUS",
(CASE
WHEN SQL_TEXT IS NULL THEN
'(SID:' || S.SID || ')会话 SQL已跑完'
ELSE
'(SID:' || S.SID || ')会话 正执行SQL' || SQL_.SQL_TEXT
END) "SQL_TEXT",
s.CLIENT_INFO,
s.program,
LO.REQUEST,
S.STATE,
S.EVENT,
S.MACHINE,
S.LOGON_TIME,
S.INST_ID,
decode(S.WAIT_TIME,-2,'不确定',-1,'<0.1s',0,to_char(S.SECONDS_IN_WAIT)||'s',to_char(S.WAIT_TIME)||'s') AS "",
S.BLOCKING_SESSION,
s.PROGRAM,
S.SID || ',' || S.SERIAL# ||',@'||S.inst_id as sessionStr,
DECODE(S.BLOCKING_SESSION,
'',
'ALTER SYSTEM KILL SESSION ''' || S.SID || ',' ||
S.SERIAL# ||''' immediate ;','') AS norac_KILL,
DECODE(S.BLOCKING_SESSION,
'',
'ALTER SYSTEM KILL SESSION ''' || S.SID || ',' ||
S.SERIAL# ||',@'||S.inst_id||''' immediate ;','') rac_KILL
FROM GV$SESSION S
LEFT JOIN GV$SQL SQL_
ON SQL_.SQL_ID = S.SQL_ID
AND SQL_.INST_ID = S.INST_ID
JOIN GV$LOCKED_OBJECT L
ON L.SESSION_ID = S.SID
AND L.INST_ID = S.INST_ID
JOIN ALL_OBJECTS O
ON L.OBJECT_ID = O.OBJECT_ID
JOIN GV$LOCK LO
ON (LO.BLOCK != 0 OR LO.REQUEST != 0)
WHERE LO.SID = L.SESSION_ID
AND LO.SID = S.SID
AND LO.INST_ID = L.INST_ID
AND LO.INST_ID = S.INST_ID
ORDER BY s.inst_id, S.BLOCKING_SESSION,s.SID DESC
]]>
</select>
<select id="getDeadLockSessionV3Slow" resultType="com.neuhis.his.domain.dto.DeadLock">
<![CDATA[
select distinct t.sid_serial,t.* from (
Select '节点 ' || a.INST_ID || ' session ' || a.sid || ',' || a_s.SERIAL# ||
' 阻塞了 节点 ' || b.INST_ID || ' session ' || b.SID || ',' ||
b_s.SERIAL# blockinfo,
('alter system kill session '''||a.sid||','||a_s.SERIAL#||''';') as "批量杀锁表进程",
a.sid||','||a_s.SERIAL# as sessionStr,
a.sid||a_s.SERIAL# as sid_serial,
a_s.SID,
a_s.SCHEMANAME,
a_s.MODULE,
a_s.STATUS,
a.type lock_type,
a.id1,
a.id2,
decode(a.lmode,
0,
'none',
1,
null,
2,
'row-S (SS)',
3,
'row-X (SX)',
4,
'share (S)',
5,
'S/Row-X (SSX)',
6,
'exclusive (X)') lock_mode,
'后为被阻塞信息',
b.INST_ID blocked_inst_id,
b_s.SID blocked_sid,
b.TYPE blocked_lock_type,
decode(b.request,
0,
'none',
1,
null,
2,
'row-S (SS)',
3,
'row-X (SX)',
4,
'share (S)',
5,
'S/Row-X (SSX)',
6,
'exclusive (X)') blocked_lock_request,
b_s.SCHEMANAME blocked_SCHEMANAME,
b_s.MODULE blocked_module,
b_s.STATUS blocked_status,
b_s.SQL_ID blocked_sql_id,
obj.owner blocked_owner,
obj.object_name blocked_object_name,
obj.OBJECT_TYPE blocked_OBJECT_TYPE,
case
when b_s.ROW_WAIT_OBJ# <> -1 then
dbms_rowid.rowid_create(1,
obj.DATA_OBJECT_ID,
b_s.ROW_WAIT_FILE#,
b_s.ROW_WAIT_BLOCK#,
b_s.ROW_WAIT_ROW#)
else
'-1'
end blocked_rowid, --被阻塞数据的rowid
decode(obj.object_type,
'TABLE',
'select * from ' || obj.owner || '.' || obj.object_name ||
' where rowid=''' ||
dbms_rowid.rowid_create(1,
obj.DATA_OBJECT_ID,
b_s.ROW_WAIT_FILE#,
b_s.ROW_WAIT_BLOCK#,
b_s.ROW_WAIT_ROW#) || '''',
NULL) blocked_data_querysql
from gv$lock a,
gv$lock b,
gv$session a_s,
gv$session b_s,
dba_objects obj
where a.id1 = b.id1
and a.id2 = b.id2
and a.BLOCK > 0 --阻塞了其他人
and b.request > 0
and ((a.INST_ID = b.INST_ID and a.sid <> b.sid) or
(a.INST_ID <> b.INST_ID))
and a.sid = a_s.sid
and a.INST_ID = a_s.INST_ID
and b.sid = b_s.sid
and b.INST_ID = b_s.INST_ID
and b_s.ROW_WAIT_OBJ# = obj.object_id(+)
order by a.inst_id, a.sid
) t -- 包裹一层,避免杀进程语句重复
]]>
</select>
<update id="killSession" parameterType="java.lang.String">
alter system kill session '${sessionStr}'
</update>

View File

@ -20,5 +20,8 @@ public enum DataSourceType
Db1,
Db2,
Db3,
Db4;
Db4,
Db5,
hitapp
;
}

21
start_NeuHis_dev.sh Normal file
View File

@ -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

10
stop_NeuHis_dev.sh Normal file
View File

@ -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