加群联系作者vx:xiaoda0423
仓库地址:https://webvueblog.github.io/JavaPlusDoc/
https://1024bat.cn/
https://github.com/webVueBlog/fastapi_plus
https://webvueblog.github.io/JavaPlusDoc/
1747398531175.png命令 |
作用 |
---|---|
top |
实时查看 CPU、内存、进程占用等 |
htop |
top 的增强版,图形化显示 |
ps |
查看当前系统进程状态 |
uptime |
查看系统运行时长与负载情况 |
vmstat |
查看内存、CPU、IO 等系统性能 |
iostat |
查看磁盘 IO 使用率(来自 |
free |
查看内存使用情况 |
sar |
收集系统性能历史(需安装 |
命令 |
作用 |
---|---|
netstat |
查看网络连接、端口监听等(建议用 |
ss |
更快更准确的替代 |
ping |
测试网络连通性 |
traceroute |
路由追踪 |
dig/nslookup |
域名解析工具 |
curl/wget |
网络请求测试 |
tcpdump |
抓包工具 |
iftop |
实时网络带宽监控 |
命令 |
作用 |
---|---|
df |
查看磁盘挂载与剩余空间 |
du |
查看目录/文件占用大小 |
lsblk |
查看磁盘挂载结构 |
mount/umount |
挂载/卸载磁盘 |
命令 |
作用 |
---|---|
ls , |
文件操作 |
find , |
查找与文本处理 |
chmod , |
修改权限与所有者 |
tar , |
压缩解压 |
systemctl , |
服务管理 |
journalctl |
查看系统日志 |
crontab |
定时任务 |
kill , |
杀进程 |
命令 |
用途说明 |
---|---|
top |
实时查看 CPU、内存占用、各进程运行状态,常用于排查系统负载高的问题 |
ps aux |
快照方式查看所有进程状态,可结合 |
netstat -tulnp |
查看监听端口与绑定服务(被 |
df -h |
查看各挂载分区的剩余空间、使用率,判断磁盘是否满 |
du -sh * |
查看当前目录下各子目录/文件大小,定位“谁占满了磁盘” |
iostat -x 1 |
查看磁盘读写速率、IO 等待情况,分析 IO 瓶颈问题(需安装 |
分类 |
命令示例 |
使用场景说明 |
---|---|---|
CPU/内存排查 |
top , |
查看高负载进程、内存泄漏、进程异常 |
磁盘排查 |
df -h , |
查看磁盘是否满、哪些文件占用最大空间 |
IO 排查 |
iostat -x 1 , |
判断磁盘 IO 是否瓶颈、IO 等待高 |
网络排查 |
netstat -tulnp , |
查看端口监听情况、网络连接状态 |
连通性测试 |
ping , |
网络是否通,DNS 解析是否正常 |
文件定位 |
find . -type f -name "*.log" , `du -ah |
sort -rh` |
日志查看 |
tail -f /var/log/messages , |
实时查看系统/服务日志,查启动失败 |
权限排查 |
ls -l , |
文件/目录权限错误导致无法访问 |
⚙️ 进程控制 |
ps , |
查找并终止异常进程 |
定时任务 |
crontab -l , |
定时脚本未触发或配置错误 |
有一次线上系统 CPU 飙到 100%,我使用
top
查看是某个 Java 进程占满 CPU,再结合ps -ef
确认 PID,之后用jstack
dump 栈分析是某个线程死循环。修复后 CPU 降回正常。
某次服务突然挂掉,
df -h
一看/var
分区 100%,du -sh *
逐级定位,发现是日志文件打满了。用logrotate
加了压缩与清理策略,避免再次发生。
应用连不上 MySQL,先用
ping
确认网络通,再telnet host 3306
发现端口不通,用ss -lntp
查服务未监听,原来是数据库未启动,systemctl start mysql
后恢复正常。
多 Redis 实例启动失败,
netstat -tulnp
显示端口已被占用,ps aux | grep redis
查出是老进程未关闭,kill
后再重启成功。
某服务响应慢但 CPU 占用低,用
iostat -x 1
查看发现磁盘util
长时间接近 100%,排查是频繁写入日志导致 IO 拥塞,优化后日志按级别写入,响应恢复。
命令 |
面试中简洁描述 |
---|---|
top |
实时监控进程 CPU、内存使用情况 |
ps aux |
查看所有进程的静态快照 |
netstat / |
查看端口监听与网络连接 |
df -h |
查看磁盘分区使用情况 |
du -sh * |
查看目录大小,排查大文件 |
iostat |
查看磁盘 IO 使用与瓶颈 |
tail -f |
实时查看日志输出 |
ping , |
检查网络连通性与服务是否在线 |
kill -9 |
强制结束进程 |
# IO 和网络工具合集(Debian/Ubuntu)
sudo apt install sysstat iotop iftop net-tools lsof
# CentOS/RHEL
sudo yum install sysstat iotop iftop net-tools lsof
排查场景 |
使用命令 |
目的 |
实际作用 / 结果 |
---|---|---|---|
Java 进程 CPU 飙升 |
top ps aux --sort=-%cpu jstack PID |
找出高 CPU 占用进程,并用 jstack 抓线程堆栈 |
确认是某线程死循环导致 CPU 占满,分析定位业务逻辑问题 |
磁盘写入异常 |
df -h |
查看分区是否已满 |
/var 分区 100%,服务日志无法写入,导致接口报错 |
大文件定位 |
du -sh * `du -sh ./* |
sort -rh |
head` |
磁盘 IO 性能瓶颈 |
iostat -x 1 |
查看 IO 利用率、等待时间 |
util 长期 90%+, |
端口冲突 / Redis 未监听 |
netstat -tulnp ss -lntp |
查看端口占用情况 |
发现 Redis 配置监听端口未启动或端口被其他进程占用 |
进程是否存在 / 多开 |
`ps aux |
grep redis |
确认进程是否已运行、是否重复启动 |
有次线上系统响应变慢,我用 top
查到 Java 进程 CPU 99%,结合 jstack
抓取线程栈,确认某死循环逻辑导致占用;也遇到磁盘异常,df
发现 /var
分区满了,用 du
快速找到是日志爆量;另外还用 iostat
定位 IO 等待高,结合日志改写策略缓解了瓶颈。
在 MongoDB 中,我们不会说“库”和“表”,而是使用以下术语:
通用数据库术语 |
MongoDB 中的术语 |
---|---|
数据库(Database) |
数据库(Database) ✅ |
表(Table) |
集合(Collection) ✅ |
行(Row) |
文档(Document) ✅ |
列(Column) |
字段(Field) ✅ |
MongoDB Server
└── Database(数据库)
└── Collection(集合,相当于表)
└── Document(文档,相当于行)
└── Field(字段,相当于列)
use mydb // 选择或创建数据库
db.createCollection("users") // 创建集合,相当于创建表
db.users.insertOne({
name: "张三",
age: 28,
email: "[email protected]"
})
db.users.find({ age: { $gt: 25 } })
MongoDB(文档型) |
MySQL / PostgreSQL(关系型) |
---|---|
无固定表结构 |
表结构需提前定义字段类型 |
支持嵌套文档、数组字段 |
不支持嵌套对象 |
灵活写入,字段可不同 |
每行字段一致,强类型约束 |
使用 JSON/BSON 存储文档 |
使用行列式存储 |
如果你想查看 Mongo 当前所有库/表(集合):
show dbs
show collections
下面是一个完整的 MongoDB 示例项目结构,模拟常见的 用户(users)+ 订单(orders) 模型,适合实际开发用作项目初始化:
数据库:demo_app
集合:
users
:用户信息
orders
:用户订单(包含用户 ID 作为外键)
你可以复制粘贴到 Mongo Shell / mongosh
中运行:
// 1. 切换或创建数据库
use demo_app
// 2. 创建 users 集合并插入示例文档
db.createCollection("users")
db.users.insertMany([
{
_id: ObjectId(),
username: "alice",
email: "[email protected]",
phone: "1234567890",
createdAt: new Date()
},
{
_id: ObjectId(),
username: "bob",
email: "[email protected]",
phone: "9876543210",
createdAt: new Date()
}
])
// 3. 创建 orders 集合并插入示例文档
db.createCollection("orders")
db.orders.insertMany([
{
_id: ObjectId(),
userId: db.users.findOne({username: "alice"})._id,
item: "iPhone 15",
price: 7999,
status: "PAID",
createdAt: new Date()
},
{
_id: ObjectId(),
userId: db.users.findOne({username: "bob"})._id,
item: "MacBook Air",
price: 9999,
status: "SHIPPED",
createdAt: new Date()
}
])
// 4. 建立索引(如用户邮箱唯一)
db.users.createIndex({ email: 1 }, { unique: true })
db.orders.createIndex({ userId: 1 })
const user = db.users.findOne({ username: "alice" })
db.orders.find({ userId: user._id })
db.orders.aggregate([
{
$lookup: {
from: "users",
localField: "userId",
foreignField: "_id",
as: "userInfo"
}
},
{
$unwind: "$userInfo"
},
{
$project: {
item: 1,
price: 1,
status: 1,
"userInfo.username": 1,
"userInfo.email": 1
}
}
])
如果 MongoDB 中的订单集合(orders)超过 1000 万条数据,想要维持查询性能和系统可扩展性,必须从多个维度进行优化:
例如常见查询条件:
db.orders.find({ userId: xxx, status: "PAID" })
应建立:
db.orders.createIndex({ userId: 1, status: 1 })
createdAt
用于分页/范围查询:db.orders.find({ createdAt: { $gte: ISODate('2024-01-01') } }).sort({ createdAt: -1 }).limit(50)
应建立:
db.orders.createIndex({ createdAt: -1 })
explain()
分析索引命中:db.orders.find({ userId: xxx }).explain("executionStats")
不要使用
skip
深分页,性能会指数下降。
db.orders.find().skip(1000000).limit(10)
使用 createdAt
或 _id
做游标分页(基于“上次最后一条”):
db.orders.find({ createdAt: { $lt: 上一页最后时间 } }).sort({ createdAt: -1 }).limit(10)
最近 6 个月的数据保留在主 orders
表中
历史订单归档到 orders_history
表(或另一个库)
使用脚本定期归档:
db.orders.aggregate([
{ $match: { createdAt: { $lt: ISODate('2024-01-01') } } },
{ $merge: { into: "orders_history" } }
])
然后在 orders
中删除老数据。
orders_2024
, orders_2023
orders_user_0
, orders_user_1
(按 userId hash)
缺点:应用层需要动态路由查询逻辑。
避免冗余嵌套字段
字段名用短名(如 uid
替代 userId
)
不查询的字段不要建索引(浪费写入性能 + 空间)
db.orders.insertMany([...])
_id
使用自定义顺序生成 _id
,可避免默认 ObjectId 带来的分布写入放大问题(但通常影响不大)。
db.orders.stats()
查看集合大小
db.orders.totalIndexSize()
查看索引空间
mongotop
, mongostat
, Atlas Profiler
实时监控
explain()
定位慢查询
如果数据量和并发再提升,可考虑:
MongoDB 分片集群(Sharding)
热数据使用 Redis 缓存
历史数据归档到 ElasticSearch 用于检索
优化方向 |
建议 |
---|---|
索引 |
组合索引 + 创建时间索引 |
分页 |
游标分页代替 skip |
分表 |
热/冷数据分离或按时间分表 |
结构优化 |
字段精简、去冗余、文档大小控制 |
查询优化 |
使用 explain 分析索引使用情况 |
扩展方案 |
数据分片 + Redis 缓存 + ES 检索 |
你提到的三种架构优化策略是构建高并发 + 大数据量系统的核心方式,适用于订单类系统、日志系统、交易系统等场景。下面是对:
MongoDB 分片集群(Sharding)
Redis 缓存热数据
ElasticSearch 归档历史数据
三者的 作用、原理、实现方式 的详解:
将 海量数据水平拆分 到多个分片节点(shard)上,实现 读写负载均衡 + 无限扩展容量。
按照 shard key
将数据分发到不同分片
查询由 Mongos(路由器)根据 key 定向到正确 shard
客户端
│
┌─▼─┐
│mongos 路由器│← 多个,可横向扩展
└─▲─┘
│
┌─┴────────────┐
│ Config Servers │ ← 配置元数据,3 个或以上
└───────────────┘
│
┌──────────────┬──────────────┐
│ Shard1 │ Shard2 │ ... 可扩展
└──────────────┴──────────────┘
配置分片集群:mongod --shardsvr
配置 router:mongos --configdb
添加分片:
sh.addShard("shard1/host1:27017")
sh.addShard("shard2/host2:27017")
启用分片:
sh.enableSharding("demo_app")
sh.shardCollection("demo_app.orders", { userId: 1 }) // 或 createdAt
将高频读的数据(如用户最新订单、订单状态)缓存到 Redis,避免 MongoDB 频繁查询。
最近 24 小时的订单状态
用户常访问的订单详情
查询结果缓存(防止重复分页)
项目 |
说明 |
---|---|
Key 设计 |
order:{orderId} 或 |
失效策略 |
热点数据设定 1h~12h TTL 或 LRU |
一致性策略 |
写入 Mongo 后立即写入 Redis,或延迟一致性同步 |
使用方式 |
先查 Redis,无数据再查 Mongo |
// 伪代码
let data = redis.get(`order:${orderId}`)
if (!data) {
data = db.orders.findOne({ _id: orderId })
redis.set(`order:${orderId}`, JSON.stringify(data), "EX", 3600)
}
将 老订单数据(如 6 个月前) 导入到 ES,支持快速搜索、分页、复杂统计分析,而不影响 Mongo 的读写压力。
快速全文搜索(支持模糊匹配)
支持复杂聚合查询(如订单金额统计)
自带倒排索引,适合海量数据
使用同步工具导入数据,如:
mongo-connector
(已过时)
自建同步程序
Logstash + MongoDB input plugin
示例数据结构:
{
"orderId": "abc123",
"userId": "u001",
"item": "MacBook Air",
"status": "PAID",
"createdAt": "2023-05-01T12:00:00"
}
查询示例(DSL):
{
"query": {
"bool": {
"must": [
{ "match": { "status": "PAID" } },
{ "range": { "createdAt": { "lt": "2024-01-01" } } }
]
}
}
}
场景 |
使用方案 |
---|---|
实时写入、读写分布式扩展 |
MongoDB 分片集群 |
高频访问订单状态或最近列表 |
Redis 缓存 |
历史订单模糊搜索、统计分析 |
ElasticSearch 存档+查询 |
+------------------------+
| 前端 / 接口 |
+------------------------+
|
+------------+------------+
| |
+--------▼--------+ +--------▼--------+
| Redis 缓存 | | MongoDB |
| 热点订单/状态 | | 分片订单主库 |
+--------+--------+ +--------+--------+
| |
+--------▼--------+ +--------▼--------+
| ElasticSearch | | Mongo Archive |
| 历史订单检索/统计 | | 历史数据冷存储 |
+------------------+ +------------------+
你想要的 Java 实现方案,其目标是:
构建一个系统:使用 MongoDB 分片集群保存订单数据,Redis 缓存热数据,Elasticsearch 存储历史数据,并通过 Java 服务读写协调。
我们将从以下 3 个核心点实现:
目标模块 |
技术方案 |
---|---|
MongoDB 分片读写 |
使用 |
Redis 缓存加速 |
使用 |
ElasticSearch 查询 |
使用 |
org.springframework.boot
spring-boot-starter-data-mongodb
spring:
data:
mongodb:
uri: mongodb://shard1:27017,shard2:27017,shard3:27017/demo_app?replicaSet=rs0
MongoTemplate
操作:@Service
public class OrderService {
@Autowired
private MongoTemplate mongoTemplate;
public void saveOrder(Order order) {
mongoTemplate.save(order, "orders");
}
public List findOrdersByUser(String userId) {
Query query = new Query(Criteria.where("userId").is(userId));
return mongoTemplate.find(query, Order.class, "orders");
}
}
org.springframework.boot
spring-boot-starter-data-redis
spring:
redis:
host: localhost
port: 6379
@Autowired
private StringRedisTemplate redisTemplate;
public Order getOrderWithCache(String orderId) {
String cacheKey = "order:" + orderId;
String cached = redisTemplate.opsForValue().get(cacheKey);
if (cached != null) {
return JSON.parseObject(cached, Order.class);
}
// 查询 MongoDB
Order order = mongoTemplate.findById(orderId, Order.class);
if (order != null) {
redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(order), Duration.ofHours(1));
}
return order;
}
co.elastic.clients
elasticsearch-java
8.11.3
@Configuration
public class EsConfig {
@Bean
public ElasticsearchClient elasticsearchClient() {
RestClient restClient = RestClient.builder(
new HttpHost("localhost", 9200)).build();
ElasticsearchTransport transport = new RestClientTransport(
restClient, new JacksonJsonpMapper());
return new ElasticsearchClient(transport);
}
}
public List searchOldOrders(String userId) throws IOException {
LocalDateTime sixMonthsAgo = LocalDateTime.now().minusMonths(6);
ElasticsearchClient client = elasticsearchClient;
SearchResponse response = client.search(s -> s
.index("orders")
.query(q -> q
.bool(b -> b
.must(m -> m.term(t -> t.field("userId").value(userId)))
.must(m -> m.range(r -> r.field("createdAt").lt(JsonData.of(sixMonthsAgo.toString()))))
)
), Order.class);
return response.hits().hits().stream().map(Hit::source).toList();
}
你可以在定时任务中,定期将 Mongo 中的老订单迁移到 ES:
@Scheduled(cron = "0 0 2 * * ?")
public void archiveOldOrdersToES() {
Query query = new Query(Criteria.where("createdAt").lt(LocalDateTime.now().minusMonths(6)));
List oldOrders = mongoTemplate.find(query, Order.class, "orders");
oldOrders.forEach(order -> {
try {
elasticsearchClient.index(i -> i
.index("orders")
.id(order.getId())
.document(order));
} catch (Exception e) {
log.error("归档失败:{}", e.getMessage());
}
});
}
+---------------------+
| Java SpringBoot |
+---------------------+
| | |
Redis (缓存) | | | MongoDB (分片主库)
热点订单 | | | 实时读写
▼ ▼ ▼
Elasticsearch (归档)
6月前老订单搜索、统计分析