Java学习第41天 - 复杂查询、多表关联、索引优化与慢 SQL 调优

一、学习目标

  • 掌握 MySQL 中常见的多表关联查询方式。
  • 理解 INNER JOINLEFT JOIN、子查询、聚合查询的使用场景。
  • 能够用 MyBatis Plus 和 XML Mapper 编写复杂查询。
  • 理解索引的基本原理,知道什么时候该建索引,什么时候索引会失效。
  • 学会使用 EXPLAIN 分析 SQL 执行计划。
  • 掌握慢 SQL 的常见原因与优化思路。
  • 能把第40天的订单模块扩展成更接近真实项目的数据查询能力。

二、为什么第41天要学习复杂查询

第40天已经完成了数据库基础、表设计、MyBatis Plus 基础 CRUD 和事务入门。

但真实项目中很少只有简单的 selectByIdselectList,更多场景是:

  • 查询用户的订单列表,同时带上用户昵称。
  • 查询订单详情,同时返回订单明细。
  • 按状态、时间、金额范围组合筛选订单。
  • 统计每天订单数量和销售额。
  • 查询销量最高的商品。
  • 后台管理系统做复杂分页查询。
  • 接口变慢后,需要定位是哪条 SQL 慢。

所以第41天的重点是:从"会查数据"升级到"会写可维护、可优化的查询"。


三、多表关联查询基础

3.1 示例表结构回顾

用户表:

sql 复制代码
CREATE TABLE users (
    id BIGINT NOT NULL AUTO_INCREMENT,
    email VARCHAR(128) NOT NULL,
    nickname VARCHAR(64) NOT NULL,
    status TINYINT NOT NULL DEFAULT 1,
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY uk_users_email (email)
);

订单表:

sql 复制代码
CREATE TABLE orders (
    id BIGINT NOT NULL AUTO_INCREMENT,
    user_id BIGINT NOT NULL,
    status VARCHAR(32) NOT NULL,
    total_amount DECIMAL(12,2) NOT NULL,
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    KEY idx_orders_user_id (user_id),
    KEY idx_orders_status_created (status, created_at)
);

订单明细表:

sql 复制代码
CREATE TABLE order_items (
    id BIGINT NOT NULL AUTO_INCREMENT,
    order_id BIGINT NOT NULL,
    product_id BIGINT NOT NULL,
    quantity INT NOT NULL,
    unit_price DECIMAL(12,2) NOT NULL,
    PRIMARY KEY (id),
    KEY idx_order_items_order_id (order_id)
);

四、JOIN 查询

4.1 INNER JOIN

INNER JOIN 只返回两张表都能匹配上的数据。

查询订单及其用户信息:

sql 复制代码
SELECT
    o.id AS order_id,
    o.status,
    o.total_amount,
    o.created_at,
    u.id AS user_id,
    u.nickname,
    u.email
FROM orders o
INNER JOIN users u ON o.user_id = u.id
WHERE o.status = 'PAID'
ORDER BY o.created_at DESC;

适用场景:

  • 订单必须有用户。
  • 只关心有关联关系成立的数据。
  • 查询结果不需要保留孤立数据。

4.2 LEFT JOIN

LEFT JOIN 会保留左表数据,即使右表没有匹配记录。

查询所有用户及其订单数量:

sql 复制代码
SELECT
    u.id,
    u.nickname,
    COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.nickname;

适用场景:

  • 要显示所有用户,即使用户没有订单。
  • 后台统计列表常用。
  • 主表数据不能因为子表为空而丢失。

4.3 JOIN 常见错误

错误一:忘记 ON 条件,导致笛卡尔积。

sql 复制代码
SELECT *
FROM orders o
JOIN users u;

这会让每一条订单和每一个用户组合,数据量会暴涨。

错误二:在 LEFT JOIN 后把右表条件写进 WHERE,导致效果变成 INNER JOIN

sql 复制代码
SELECT
    u.id,
    u.nickname,
    o.id AS order_id
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE o.status = 'PAID';

如果想保留没有订单的用户,应写成:

sql 复制代码
SELECT
    u.id,
    u.nickname,
    o.id AS order_id
FROM users u
LEFT JOIN orders o
    ON u.id = o.user_id
   AND o.status = 'PAID';

五、聚合查询

5.1 COUNT 统计

统计每种订单状态的数量:

sql 复制代码
SELECT
    status,
    COUNT(*) AS count
FROM orders
GROUP BY status;

5.2 SUM 求和

统计已支付订单总金额:

sql 复制代码
SELECT
    SUM(total_amount) AS paid_amount
FROM orders
WHERE status = 'PAID';

5.3 按日期统计

统计每天订单数量和订单金额:

sql 复制代码
SELECT
    DATE(created_at) AS order_date,
    COUNT(*) AS order_count,
    SUM(total_amount) AS total_amount
FROM orders
WHERE created_at >= '2026-06-01'
GROUP BY DATE(created_at)
ORDER BY order_date ASC;

注意:GROUP BY DATE(created_at) 可读性好,但可能影响索引使用。数据量大时可以考虑增加单独的日期字段,例如 order_date

5.4 HAVING 过滤聚合结果

查询订单数大于 5 的用户:

sql 复制代码
SELECT
    user_id,
    COUNT(*) AS order_count
FROM orders
GROUP BY user_id
HAVING COUNT(*) > 5;

WHERE 是分组前过滤,HAVING 是分组后过滤。


六、子查询

6.1 IN 子查询

查询有订单的用户:

sql 复制代码
SELECT *
FROM users
WHERE id IN (
    SELECT DISTINCT user_id
    FROM orders
);

6.2 EXISTS 子查询

sql 复制代码
SELECT *
FROM users u
WHERE EXISTS (
    SELECT 1
    FROM orders o
    WHERE o.user_id = u.id
);

通常大数据量场景下,EXISTS 更适合判断是否存在,具体性能要看执行计划。

6.3 子查询与 JOIN 的选择

优先考虑 JOIN

  • 查询结果需要两张表字段。
  • 需要排序、分页、过滤组合。
  • SQL 可读性更强。

考虑子查询:

  • 只判断是否存在。
  • 查询逻辑独立。
  • 聚合后再筛选主表。

七、分页查询优化

7.1 普通分页

sql 复制代码
SELECT *
FROM orders
ORDER BY created_at DESC
LIMIT 20 OFFSET 100;

问题:OFFSET 越大,数据库需要跳过的数据越多,深分页会越来越慢。

7.2 深分页问题

sql 复制代码
SELECT *
FROM orders
ORDER BY id DESC
LIMIT 20 OFFSET 100000;

数据库可能需要扫描前 100000 条,再返回 20 条。

7.3 游标分页

sql 复制代码
SELECT *
FROM orders
WHERE id < 100000
ORDER BY id DESC
LIMIT 20;

接口参数可以从:

text 复制代码
page=0&size=20

升级为:

text 复制代码
cursor=100000&size=20

适合场景:

  • 信息流。
  • 消息列表。
  • 日志列表。
  • 订单流水列表。
  • 不要求精确跳到第 N 页的场景。

7.4 延迟关联优化

深分页时可以先查主键,再关联完整数据:

sql 复制代码
SELECT o.*
FROM orders o
INNER JOIN (
    SELECT id
    FROM orders
    ORDER BY id DESC
    LIMIT 20 OFFSET 100000
) t ON o.id = t.id;

八、索引基础

8.1 索引是什么

索引可以理解为数据库的目录。

没有索引时:

sql 复制代码
SELECT * FROM orders WHERE user_id = 100;

数据库可能要从第一行扫到最后一行。

有索引时:

sql 复制代码
KEY idx_orders_user_id (user_id)

数据库可以通过索引快速定位 user_id = 100 的订单。

8.2 常见索引类型

类型 说明 示例
主键索引 唯一且非空 PRIMARY KEY (id)
唯一索引 值不能重复 UNIQUE KEY uk_email (email)
普通索引 加速查询 KEY idx_user_id (user_id)
联合索引 多列组成一个索引 KEY idx_status_created (status, created_at)

8.3 什么时候建索引

适合建索引的字段:

  • 经常出现在 WHERE 条件中。
  • 经常用于 JOIN ON
  • 经常用于 ORDER BY
  • 区分度较高的字段。
  • 唯一性约束字段。

例如:

sql 复制代码
CREATE INDEX idx_orders_user_status
ON orders(user_id, status);

适合查询:

sql 复制代码
SELECT *
FROM orders
WHERE user_id = 1
  AND status = 'PAID';

8.4 不适合建索引的字段

不太适合建索引:

  • 数据量很小的表。
  • 频繁更新但很少查询的字段。
  • 区分度很低的字段,例如性别、布尔值。
  • 大文本字段,如 TEXT

九、联合索引与最左前缀原则

9.1 联合索引示例

sql 复制代码
CREATE INDEX idx_orders_user_status_created
ON orders(user_id, status, created_at);

这个索引可以用于:

sql 复制代码
WHERE user_id = 1
sql 复制代码
WHERE user_id = 1 AND status = 'PAID'
sql 复制代码
WHERE user_id = 1 AND status = 'PAID' ORDER BY created_at DESC

不适合直接用于:

sql 复制代码
WHERE status = 'PAID'

因为跳过了最左边的 user_id

9.2 最左前缀原则

联合索引 (a, b, c) 可以命中:

  • a
  • a, b
  • a, b, c

一般不能很好命中:

  • b
  • c
  • b, c

9.3 范围查询后的字段

sql 复制代码
CREATE INDEX idx_orders_user_created_status
ON orders(user_id, created_at, status);

查询:

sql 复制代码
SELECT *
FROM orders
WHERE user_id = 1
  AND created_at >= '2026-06-01'
  AND status = 'PAID';

created_at 是范围条件,后面的 status 可能不能充分利用索引。

所以联合索引字段顺序要根据查询场景设计。


十、索引失效常见场景

10.1 对索引列使用函数

sql 复制代码
SELECT *
FROM orders
WHERE DATE(created_at) = '2026-06-10';

优化:

sql 复制代码
SELECT *
FROM orders
WHERE created_at >= '2026-06-10 00:00:00'
  AND created_at < '2026-06-11 00:00:00';

10.2 左模糊查询

sql 复制代码
SELECT *
FROM users
WHERE email LIKE '%example.com';

普通 BTree 索引难以利用。

可以利用索引:

sql 复制代码
SELECT *
FROM users
WHERE email LIKE 'admin%';

10.3 隐式类型转换

字段是字符串:

sql 复制代码
email VARCHAR(128)

错误写法:

sql 复制代码
SELECT *
FROM users
WHERE email = 123;

应该写成:

sql 复制代码
SELECT *
FROM users
WHERE email = '123';

10.4 OR 条件使用不当

sql 复制代码
SELECT *
FROM orders
WHERE user_id = 1
   OR status = 'PAID';

如果两个字段索引设计不合理,可能导致全表扫描。

可以根据场景改成 UNION ALL

sql 复制代码
SELECT *
FROM orders
WHERE user_id = 1
UNION ALL
SELECT *
FROM orders
WHERE status = 'PAID';

注意:UNION ALL 不去重,如果可能重复,需要额外处理。


十一、EXPLAIN 执行计划

11.1 基本用法

sql 复制代码
EXPLAIN
SELECT *
FROM orders
WHERE user_id = 1
ORDER BY created_at DESC;

11.2 重点字段

字段 含义
id 查询执行顺序标识
select_type 查询类型
table 当前访问的表
type 访问类型,越接近 const 越好
possible_keys 可能使用的索引
key 实际使用的索引
rows 预估扫描行数
Extra 额外信息

11.3 type 常见级别

从好到差大致为:

text 复制代码
system > const > eq_ref > ref > range > index > ALL

重点关注:

  • ALL:全表扫描,数据量大时危险。
  • index:扫整个索引,也可能很慢。
  • range:范围扫描,通常可以接受。
  • ref:普通索引等值查询,较好。
  • const:主键或唯一索引命中,非常好。

11.4 Extra 常见信息

Extra 说明
Using index 使用覆盖索引
Using where 使用 WHERE 过滤
Using temporary 使用临时表
Using filesort 额外排序
Using index condition 使用索引条件下推

看到 Using temporaryUsing filesort 不一定必然有问题,但在大数据量查询中要重点关注。


十二、覆盖索引

12.1 什么是覆盖索引

查询需要的字段都在索引中,不需要回表。

索引:

sql 复制代码
CREATE INDEX idx_orders_user_status_created
ON orders(user_id, status, created_at);

查询:

sql 复制代码
SELECT user_id, status, created_at
FROM orders
WHERE user_id = 1
  AND status = 'PAID';

这可能形成覆盖索引。

12.2 避免 SELECT *

sql 复制代码
SELECT *
FROM orders
WHERE user_id = 1;

如果只需要部分字段,应写成:

sql 复制代码
SELECT id, status, total_amount, created_at
FROM orders
WHERE user_id = 1;

好处:

  • 减少网络传输。
  • 有机会使用覆盖索引。
  • 避免表结构变化影响接口。

十三、MyBatis Plus 复杂查询

13.1 LambdaQueryWrapper 动态条件

java 复制代码
public List<OrderEntity> searchOrders(Long userId, String status, LocalDateTime start, LocalDateTime end) {
    LambdaQueryWrapper<OrderEntity> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(userId != null, OrderEntity::getUserId, userId)
            .eq(StringUtils.hasText(status), OrderEntity::getStatus, status)
            .ge(start != null, OrderEntity::getCreatedAt, start)
            .lt(end != null, OrderEntity::getCreatedAt, end)
            .orderByDesc(OrderEntity::getCreatedAt);

    return orderMapper.selectList(wrapper);
}

优点:

  • 字段引用类型安全。
  • 动态条件写法清晰。
  • 适合单表查询。

13.2 分页查询

java 复制代码
public Page<OrderEntity> pageOrders(int page, int size, String status) {
    LambdaQueryWrapper<OrderEntity> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(StringUtils.hasText(status), OrderEntity::getStatus, status)
            .orderByDesc(OrderEntity::getCreatedAt);

    return orderMapper.selectPage(new Page<>(page, size), wrapper);
}

注意:MyBatis Plus 的页码从 1 开始。如果接口使用从 0 开始的页码,需要转换。

java 复制代码
int current = page + 1;

十四、XML Mapper 复杂查询

当查询涉及多表关联、聚合统计、复杂动态条件时,XML 比 Wrapper 更清晰。

14.1 DTO 定义

java 复制代码
public class OrderAdminPageDTO {
    private Long orderId;
    private Long userId;
    private String nickname;
    private String status;
    private BigDecimal totalAmount;
    private LocalDateTime createdAt;

    // getter setter
}

14.2 Mapper 接口

java 复制代码
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.time.LocalDateTime;
import java.util.List;

@Mapper
public interface OrderReportMapper {

    List<OrderAdminPageDTO> selectAdminPage(
            @Param("userId") Long userId,
            @Param("status") String status,
            @Param("startTime") LocalDateTime startTime,
            @Param("endTime") LocalDateTime endTime,
            @Param("offset") int offset,
            @Param("size") int size
    );

    long countAdminPage(
            @Param("userId") Long userId,
            @Param("status") String status,
            @Param("startTime") LocalDateTime startTime,
            @Param("endTime") LocalDateTime endTime
    );
}

14.3 XML 查询

xml 复制代码
<mapper namespace="com.company.app.mapper.OrderReportMapper">

    <select id="selectAdminPage" resultType="com.company.app.dto.OrderAdminPageDTO">
        SELECT
            o.id AS orderId,
            o.user_id AS userId,
            u.nickname AS nickname,
            o.status AS status,
            o.total_amount AS totalAmount,
            o.created_at AS createdAt
        FROM orders o
        INNER JOIN users u ON o.user_id = u.id
        <where>
            <if test="userId != null">
                AND o.user_id = #{userId}
            </if>
            <if test="status != null and status != ''">
                AND o.status = #{status}
            </if>
            <if test="startTime != null">
                AND o.created_at &gt;= #{startTime}
            </if>
            <if test="endTime != null">
                AND o.created_at &lt; #{endTime}
            </if>
        </where>
        ORDER BY o.created_at DESC
        LIMIT #{size} OFFSET #{offset}
    </select>

    <select id="countAdminPage" resultType="long">
        SELECT COUNT(*)
        FROM orders o
        INNER JOIN users u ON o.user_id = u.id
        <where>
            <if test="userId != null">
                AND o.user_id = #{userId}
            </if>
            <if test="status != null and status != ''">
                AND o.status = #{status}
            </if>
            <if test="startTime != null">
                AND o.created_at &gt;= #{startTime}
            </if>
            <if test="endTime != null">
                AND o.created_at &lt; #{endTime}
            </if>
        </where>
    </select>

</mapper>

14.4 Service 封装分页

java 复制代码
@Service
public class OrderAdminQueryService {

    private final OrderReportMapper orderReportMapper;

    public OrderAdminQueryService(OrderReportMapper orderReportMapper) {
        this.orderReportMapper = orderReportMapper;
    }

    public PageResult<OrderAdminPageDTO> search(OrderSearchRequest req) {
        int safeSize = Math.min(Math.max(req.getSize(), 1), 100);
        int safePage = Math.max(req.getPage(), 0);
        int offset = safePage * safeSize;

        List<OrderAdminPageDTO> list = orderReportMapper.selectAdminPage(
                req.getUserId(),
                req.getStatus(),
                req.getStartTime(),
                req.getEndTime(),
                offset,
                safeSize
        );

        long total = orderReportMapper.countAdminPage(
                req.getUserId(),
                req.getStatus(),
                req.getStartTime(),
                req.getEndTime()
        );

        return PageResult.of(list, total, safePage, safeSize);
    }
}

十五、统计接口示例

15.1 每日订单统计 DTO

java 复制代码
public class DailyOrderStatsDTO {
    private LocalDate orderDate;
    private Long orderCount;
    private BigDecimal totalAmount;

    // getter setter
}

15.2 Mapper

java 复制代码
List<DailyOrderStatsDTO> selectDailyStats(
        @Param("startDate") LocalDate startDate,
        @Param("endDate") LocalDate endDate
);

15.3 XML

xml 复制代码
<select id="selectDailyStats" resultType="com.company.app.dto.DailyOrderStatsDTO">
    SELECT
        DATE(o.created_at) AS orderDate,
        COUNT(*) AS orderCount,
        SUM(o.total_amount) AS totalAmount
    FROM orders o
    WHERE o.created_at &gt;= #{startDate}
      AND o.created_at &lt; DATE_ADD(#{endDate}, INTERVAL 1 DAY)
    GROUP BY DATE(o.created_at)
    ORDER BY orderDate ASC
</select>

优化建议:

如果订单量很大,建议新增字段:

sql 复制代码
order_date DATE NOT NULL

然后查询:

sql 复制代码
WHERE order_date >= '2026-06-01'
  AND order_date <= '2026-06-30'
GROUP BY order_date

并建立索引:

sql 复制代码
CREATE INDEX idx_orders_order_date
ON orders(order_date);

十六、慢 SQL 常见原因

16.1 没有索引

sql 复制代码
SELECT *
FROM orders
WHERE user_id = 100;

如果 user_id 没有索引,大表会全表扫描。

16.2 索引设计不匹配

索引:

sql 复制代码
KEY idx_status_created (status, created_at)

查询:

sql 复制代码
SELECT *
FROM orders
WHERE created_at >= '2026-06-01';

这个查询没有使用联合索引的最左列 status,效果可能不好。

16.3 返回数据过多

sql 复制代码
SELECT *
FROM orders
WHERE status = 'PAID';

如果 PAID 占全表 80%,即使有索引也可能不快。

16.4 排序字段无索引

sql 复制代码
SELECT *
FROM orders
WHERE user_id = 1
ORDER BY created_at DESC;

建议索引:

sql 复制代码
CREATE INDEX idx_orders_user_created
ON orders(user_id, created_at);

16.5 深分页

sql 复制代码
LIMIT 20 OFFSET 500000

考虑游标分页或延迟关联。

16.6 大事务占用锁

长时间事务可能阻塞其他更新操作。


十七、慢 SQL 优化流程

17.1 第一步:定位 SQL

来源:

  • 应用日志。
  • MySQL 慢查询日志。
  • APM 监控。
  • 接口耗时日志。
  • 数据库监控平台。

17.2 第二步:查看执行计划

sql 复制代码
EXPLAIN SELECT ...

重点看:

  • type 是否为 ALL
  • key 是否为空。
  • rows 是否过大。
  • Extra 是否出现 Using temporaryUsing filesort

17.3 第三步:判断是否需要索引

常见策略:

  • 等值过滤字段放前面。
  • 范围字段放后面。
  • 排序字段尽量纳入联合索引。
  • 避免建太多重复索引。

17.4 第四步:改 SQL

常见改法:

  • 减少 SELECT *
  • 避免函数包裹索引列。
  • 大分页改游标分页。
  • 拆分复杂 SQL。
  • 预聚合统计数据。
  • 用批量查询替代循环查询。

17.5 第五步:回归测试

优化后要确认:

  • 结果没有变化。
  • 执行计划更合理。
  • 接口耗时下降。
  • 写入性能没有明显下降。

十八、接口层查询设计

18.1 查询参数设计

后台订单查询接口:

http 复制代码
GET /api/v1/admin/orders?page=0&size=20&status=PAID&userId=1&startTime=2026-06-01T00:00:00&endTime=2026-07-01T00:00:00

对应 Request:

java 复制代码
public class OrderSearchRequest {
    private int page = 0;
    private int size = 20;
    private Long userId;
    private String status;
    private LocalDateTime startTime;
    private LocalDateTime endTime;

    // getter setter
}

18.2 Controller

java 复制代码
@RestController
@RequestMapping("/api/v1/admin/orders")
public class OrderAdminController {

    private final OrderAdminQueryService orderAdminQueryService;

    public OrderAdminController(OrderAdminQueryService orderAdminQueryService) {
        this.orderAdminQueryService = orderAdminQueryService;
    }

    @GetMapping
    public PageResult<OrderAdminPageDTO> search(OrderSearchRequest request) {
        return orderAdminQueryService.search(request);
    }
}

18.3 参数校验

java 复制代码
public class OrderSearchRequest {

    @Min(0)
    private int page = 0;

    @Min(1)
    @Max(100)
    private int size = 20;

    private Long userId;

    private String status;

    private LocalDateTime startTime;

    private LocalDateTime endTime;

    // getter setter
}

十九、避免 N+1 查询

19.1 反例

java 复制代码
List<OrderEntity> orders = orderMapper.selectList(wrapper);

for (OrderEntity order : orders) {
    UserEntity user = userMapper.selectById(order.getUserId());
    order.setUserName(user.getNickname());
}

如果有 100 条订单,会查询 1 次订单加 100 次用户。

19.2 批量查询优化

java 复制代码
List<Long> userIds = orders.stream()
        .map(OrderEntity::getUserId)
        .distinct()
        .toList();

List<UserEntity> users = userMapper.selectBatchIds(userIds);

Map<Long, UserEntity> userMap = users.stream()
        .collect(Collectors.toMap(UserEntity::getId, Function.identity()));

for (OrderEntity order : orders) {
    UserEntity user = userMap.get(order.getUserId());
    if (user != null) {
        order.setUserName(user.getNickname());
    }
}

19.3 直接 JOIN 查询

如果接口本来就需要用户信息,可以直接使用 JOIN 查询返回 DTO。


二十、数据库与业务边界

20.1 不要把所有业务逻辑塞进 SQL

适合放 SQL 的逻辑:

  • 过滤。
  • 排序。
  • 分页。
  • 聚合。
  • 简单计算。

适合放 Java 的逻辑:

  • 权限判断。
  • 状态流转。
  • 复杂业务规则。
  • 调用外部服务。
  • 领域对象组装。

20.2 查询 DTO 不等于实体 Entity

实体 OrderEntity 对应数据库表。

接口返回 OrderAdminPageDTO 对应前端需要的数据。

不要为了接口方便随意改实体结构。

相关推荐
llz_1121 小时前
web-第五次课后作业
前端·后端·http
雨辰AI2 小时前
生产级实测:SpringBoot3 + 达梦数据库接口从 200ms 优化至 20ms 完整调优指南
java·数据库·spring boot·后端·政务
Solis2 小时前
Raft:分布式系统的定海神针
后端·架构
程序员老申2 小时前
第三篇 5 天 12 个 commit:踩坑实录与代码演进
后端·程序员
程序员鱼皮2 小时前
提示词工程已死,Loop Engineering 称王!保姆级教程 + 项目实战
前端·后端·ai编程
Mininglamp_27183 小时前
Vibe Coding 之后是 Vibe Operating?
后端·开源·多智能体·ai agent·mano-p
星哥的编程之路3 小时前
别再调 API 就说自己会 RAG 了,看看真正的企业级 AI 智能体长什么样
后端·面试
长大19883 小时前
C++26 静态反射完整实战:告别宏代码生成,一键实现序列化
后端
yb7793 小时前
Java 21 虚拟线程最佳实践:虚拟线程如何让高并发 Java 服务更轻更快
后端