1 问题
测试进行压力测试发现TPS测不上去,查看某台服务器cpu负荷较高
2 分析
使用top查找占用cpu较高的进程,并进一步查看是哪个线程导致的
top
top -Hp <pid>
......
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1087260 lxd 20 0 3247060 999164 63968 S 67.3 3.0 0:53.43 conn561721 ------conn4561721中的561721 为mongo connectionid
......
查看connectionID执行的mongo操作,结果显示对dynamic_workflow.form_data进行了查询操作,并进行了全表扫描,可通过添加索引解决。
rs:PRIMARY> db.currentOp({connectionId:561721}).inprog
{
"type" : "op",
"host" : "mysql70:27017",
"desc" : "conn561721",
"connectionId" : 561721,
"client" : "192.168.99.65:58589",
"clientMetadata" : {
"driver" : {
"name" : "mongo-java-driver|sync|spring-boot",
"version" : "4.6.1"
},
"os" : {
"type" : "Linux",
"name" : "Linux",
"architecture" : "amd64",
"version" : "5.15.0-60-generic"
},
"platform" : "Java/Oracle Corporation/17.0.2+8-86"
},
"active" : true,
"currentOpTime" : "2024-05-14T09:09:29.664+08:00",
"effectiveUsers" : [
{
"user" : "jgpt_admin",
"db" : "admin"
}
],
"threaded" : true,
"opid" : 66012973,
"lsid" : {
"id" : UUID("0d8a1658-296c-4ffc-a506-c6ec022b8378"),
"uid" : BinData(0,"5xX1/ZlYeflNwIcrSmML76pjUnXqGWtUWWlg9OH2KDo=")
},
"secs_running" : NumberLong(0),
"microsecs_running" : NumberLong(68325),
"op" : "query",
"ns" : "dynamic_workflow.form_data",
"command" : {
"find" : "form_data",
"filter" : {
"delFlag" : 0,
"tenantId" : NumberLong("1697045301564059649"),
"moduleId" : NumberLong("1763122852908580866"),
"formId" : "wf8fcf718fd3c055f105c822",
"$or" : [
{
"data.field8650498512391" : {
"$nin" : [
null,
"",
{
},
[ ]
]
}
},
{
"data.field6930498536049" : {
"$nin" : [
null,
"",
{
},
[ ]
]
}
},
{
"data.field1465707611950" : {
"$nin" : [
null,
"",
{
},
[ ]
]
}
},
{
"data.field9377998559220" : {
"$nin" : [
null,
"",
{
},
[ ]
]
}
},
{
"createBy" : {
"$nin" : [
null,
"",
{
},
[ ]
]
}
},
{
"deptId" : {
"$nin" : [
null,
"",
{
},
[ ]
]
}
},
{
"createTime" : {
"$nin" : [
null,
"",
{
},
[ ]
]
}
},
{
"updateTime" : {
"$nin" : [
null,
"",
{
},
[ ]
]
}
},
{
"taskName" : {
"$nin" : [
null,
"",
{
},
[ ]
]
}
},
{
"status" : {
"$nin" : [
null,
"",
{
},
[ ]
]
}
},
{
"handleStatus" : {
"$nin" : [
null,
"",
{
},
[ ]
]
}
},
{
"handleTime" : {
"$nin" : [
null,
"",
{
},
[ ]
]
}
},
{
"_id" : {
"$nin" : [
null,
"",
{
},
""
]
}
},
{
"instanceId" : {
"$nin" : [
null,
"",
{
},
[ ]
]
}
}
]
},
"sort" : {
"updateTime" : -1,
"sort" : 1
},
"projection" : {
"data.field8650498512391" : 1,
"data.field1465707611950" : 1,
"handleTime" : 1,
"data.field6930498536049" : 1,
"deptId" : 1,
"updateTime" : 1,
"createBy" : 1,
"instanceId" : 1,
"createTime" : 1,
"handleStatus" : 1,
"taskName" : 1,
"data.field9377998559220" : 1,
"_id" : 1,
"status" : 1
},
"limit" : 10,
"collation" : {
"locale" : "zh"
},
"$db" : "dynamic_workflow",
"$clusterTime" : {
"clusterTime" : Timestamp(1715648965, 1),
"signature" : {
"hash" : BinData(0,"6q1n2rq+UEzJDwf6bXWjNSJnhzo="),
"keyId" : NumberLong("7357239627557634052")
}
},
"lsid" : {
"id" : UUID("0d8a1658-296c-4ffc-a506-c6ec022b8378")
}
},
"planSummary" : "COLLSCAN",
"numYields" : 71,
"locks" : {
"Global" : "r"
},
"waitingForLock" : false,
"lockStats" : {
"Global" : {
"acquireCount" : {
"r" : NumberLong(72)
}
添加索引
use dynamic_workflow
db.form_data.createIndex({'tenantId':1})
db.form_data.createIndex({'moduleId':1})
db.form_data.createIndex({'formId':1})
3 db.currentOp命令输出字段含义
opid: 操作的ID,是一个递增的数字,用于唯一标识一个操作。
desc: 操作的描述,通常是操作的类型,例如 “conn1” 表示一个客户端连接。
active: 一个布尔值,指示操作当前是否处于活动状态。
secs_running: 操作已经运行的秒数。
microsecs_running: 操作已经运行的微秒数,与 secs_running 结合可以提供操作运行的精确时间。
connection_id: 执行操作的客户端连接的ID。
client: 执行操作的客户端的地址,通常包括IP地址和端口号。
thread: 执行操作的线程ID。
process: 在某些系统上,这可能表示执行操作的进程ID。
command: 对于查询操作,这是被执行的命令或查询的JSON表示。
query: 对于查询操作,这可能包括查询的集合名称、过滤条件、选项等。
docs_examined: 操作检查的文档数量。
docs_returned: 操作返回的文档数量。
keys_examined: 对于索引扫描,这是检查的键的数量。
lock_type: 操作持有的锁类型,例如 “read” 或 “write”。
lock_state: 锁的状态,例如 “waiting” 或 “held”。
waiting_for_lock: 如果操作正在等待获取锁,这里会列出它正在等待的锁类型。
op: 操作的类型,如 “query”、“update”、“insert” 等。
ns: 操作影响的命名空间,通常是数据库和集合的名称组合,例如 “dbname.collectionname”。
prelock_stats: 有关操作在获取锁之前的统计信息。
prelock_data: 有关操作在获取锁之前的状态信息。
请注意,db.currentOp 命令的输出可能会根据 MongoDB 的版本和你的数据库配置有所不同。此外,db.currentOp 还可以接受一些参数来过滤输出,例如只显示运行时间超过一定秒数的操作。
为了更有效地使用 db.currentOp,你可能会结合使用 sort()、limit() 等聚合框架命令,或者使用 db.currentOp({ allUsers : true }) 来查看所有用户的活动,而不仅仅是当前用户的。