Py学习  »  DATABASE

Spring Boot + Apache Doris,抛弃MySQL开启实时分析新时代

Java知音 • 1 月前 • 80 次点击  

前言

在大数据爆发的今天,实时数据分析已成为企业决策的核心支撑 —— 电商需要实时监控爆款库存,运维需要秒级定位系统异常,营销需要实时调整投放策略。而作为传统关系型数据库的代表,MySQL 在中小规模数据存储和简单查询场景中表现稳定,但面对千万级以上数据量的复杂聚合分析(如多维度统计、漏斗转化计算)时,往往陷入查询超时、性能暴跌的困境,难以满足实时性需求。

此时,Apache Doris 这款高性能 MPP 分析型数据库应运而生,它专为实时数据分析场景设计,完美弥补了 MySQL 的短板。

本文将带大家实战 Spring Boot 与 Apache Doris 的集成方案,看看如何用它替代 MySQL,轻松搞定高并发、大数据量的实时分析需求!

救星登场:Apache Doris 闪亮来袭

什么是 Apache Doris?

Apache Doris 是一款基于 MPP(Massively Parallel Processing,大规模并行处理)架构的开源分析型数据库,起源于百度,2018 年开源并捐赠给 Apache 基金会。

它专为 OLAP(在线分析处理)场景设计,核心定位是 “实时、高效、易用”,支持 PB 级数据的低延迟查询,无需复杂的分库分表或数据预处理,就能直接应对多维度、高并发的分析需求。

核心特性,碾压 MySQL 的关键

1、极致查询性能: MPP 架构让数据在多个节点并行计算,单查询响应时间可低至毫秒级,比 MySQL 在大数据量下的查询速度快 10-100 倍;

2、实时数据接入: 支持从 Kafka、Flink 等流处理框架实时导入数据,也可同步 MySQL、Hive 等数据源,数据延迟低至秒级;

3、兼容 MySQL 协议: 无需修改业务代码中的 SQL 语法,支持 JDBC/ODBC 连接,集成成本极低;

4、高并发支持: 可轻松支撑每秒数千次的查询请求,适合面向用户的分析型应用(如数据看板、自助分析平台);

5、极简运维: 支持单节点部署,也可横向扩展至数百节点,自动分片、负载均衡,无需复杂的集群管理。

Doris vs MySQL:适用场景对比

一句话总结:MySQL 适合 “写多查少” 的业务交易场景(如订单创建、用户注册),Apache Doris 适合 “读多写少” 的实时分析场景(如数据统计、报表生成)。

Spring Boot 集成 Apache Doris 实战

前期准备

  • 环境要求:  JDK 1.8+、Spring Boot 2.3+、Maven 3.6+;
  • Apache Doris 部署: 推荐使用 Docker 快速部署(适合测试),或参考官方文档部署集群;
  • 核心依赖: Doris JDBC 驱动(兼容 MySQL 协议,可直接使用 MySQL JDBC 驱动)。

第一步:创建 Spring Boot 项目

1.访问 Spring Initializr,选择:

  • Spring Boot 版本: 2.7.x(稳定版);
  • 依赖: Spring Web(提供接口)、Spring Data JPA(简化数据操作)、MySQL Driver(兼容 Doris);

2.下载项目后,解压并导入 IDEA,项目基本结构如下:

src/
├── main/
│   ├── java/com/example/dorisdemo/
│   │   ├── controller/  // 接口层
│   │   ├── entity/      // 实体类
│   │   ├── repository/  // 数据访问层
│   │   ├── service/     // 业务层
│   │   └── DorisDemoApplication.java  // 启动类
│   └── resources/
│       └── application.yml  // 配置文件
└── pom.xml  // 依赖配置

第二步:核心配置(关键步骤)

1、 配置 pom.xml 依赖

无需额外引入 Doris 专属依赖,直接使用 MySQL JDBC 驱动即可(Doris 兼容 MySQL 协议),在 pom.xml 中添加:


<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-webartifactId>
dependency>

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-data-jpaartifactId>
dependency>

<dependency>
    <groupId>mysqlgroupId>
    <artifactId>mysql-connector-javaartifactId>
    <scope>runtimescope>
dependency>

<dependency>
    <groupId>org.projectlombokgroupId>
    <artifactId>lombokartifactId>
    <optional>trueoptional>
dependency>
2、 配置 application.yml

在  resources 目录下创建 application.yml,配置 Doris 连接信息(重点注意 url 格式):

spring:
  # 数据库配置(Doris兼容MySQL协议)
datasource:
    url:jdbc:mysql://127.0.0.1:9030/demo_db?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
    username:root# Doris默认用户名
    password:123456# 部署时设置的密码
    driver-class-name:com.mysql.cj.jdbc.Driver
# JPA配置(简化CRUD操作)
jpa:
    hibernate:
      ddl-auto:none# 关闭自动建表(Doris表手动创建)
    properties:
      hibernate:
        dialect:org.hibernate.dialect.MySQL8Dialect
        format_sql:true# 格式化SQL日志
    show-sql:true# 打印SQL语句

# 日志配置(可选)
logging:
level:
    org.springframework.data.jpa:debug
    com.example.dorisdemo:info

说明:Doris 的 JDBC 连接端口默认是 9030(FE 查询端口),demo_db是提前在 Doris 中创建的数据库。

第三步:创建 Doris 数据表

在 Doris 中手动创建测试表(以电商用户行为表为例),执行 SQL:

-- 创建数据库
CREATE DATABASEIFNOTEXISTS demo_db;
USE demo_db;

-- 创建用户行为表(分区表,按日期分区)
CREATETABLEIFNOTEXISTS user_behavior (
    user_id BIGINTCOMMENT'用户ID',
    product_id BIGINTCOMMENT'商品ID',
    category_id INTCOMMENT'商品分类ID',
    behavior_type VARCHAR(20COMMENT'行为类型(click/purchase/collect)',
    create_time DATETIME COMMENT'行为时间'
ENGINE=OLAP
DUPLICATEKEY(user_id, product_id)
PARTITIONBYRANGE(create_time) (
    PARTITION p202401 VALUESLESSTHAN ('2024-02-01'),
    PARTITION p202402 VALUESLESSTHAN ('2024-03-01')
)
DISTRIBUTEDBYHASH(user_id) BUCKETS 10
PROPERTIES (
    "storage_medium" = "HDD",
    "storage_ttl" = "30 DAY"
);

说明:Doris 支持分区表、分桶表,通过PARTITION BYDISTRIBUTED BY优化查询性能,适合大数据量存储。

第四步:编写代码实现业务逻辑

1、 实体类(Entity)

映射 Doris 的user_behavior表:

package com.example.dorisdemo.entity;

import lombok.Data;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;

import javax.persistence.*;
import java.time.LocalDateTime;

@Data
@Entity
@Table(name = "user_behavior")  // 对应Doris表名
@DynamicInsert// 动态插入(只插入非空字段)
@DynamicUpdate// 动态更新(只更新修改字段)
publicclass UserBehavior {

    @Id// 主键(Doris无自增主键,需手动指定)
    @Column(name = "user_id")
    private Long userId;

    @Column(name = "product_id")
    private Long productId;

    @Column(name = "category_id")
    private Integer categoryId;

    @Column(name = "behavior_type")
    private String behaviorType;  // click/purchase/collect

    @Column(name = "create_time")
    private LocalDateTime createTime;
}
2、 数据访问层(Repository)

继承 JpaRepository,简化 CRUD 操作:




    
package com.example.dorisdemo.repository;

import com.example.dorisdemo.entity.UserBehavior;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

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

@Repository
publicinterface UserBehaviorRepository extends JpaRepository<UserBehaviorLong{

    // 自定义查询:查询指定日期范围内的用户行为
    List findByCreateTimeBetween(LocalDateTime start, LocalDateTime end);

    // 复杂查询:统计指定分类的商品点击量(Doris优化的聚合查询)
    @Query(value = "SELECT COUNT(*) FROM user_behavior WHERE category_id = :categoryId AND behavior_type = 'click' AND create_time BETWEEN :start AND :end", nativeQuery = true)
    Long countClickByCategoryId(@Param("categoryId") Integer categoryId, 
                                @Param("start") LocalDateTime start, 
                                @Param("end") LocalDateTime end)
;
}
3、 业务层(Service)

实现核心业务逻辑:

package com.example.dorisdemo.service;

import com.example.dorisdemo.entity.UserBehavior;
import com.example.dorisdemo.repository.UserBehaviorRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

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

@Service
@RequiredArgsConstructor// 构造器注入依赖
publicclass UserBehaviorService {

    privatefinal UserBehaviorRepository behaviorRepository;

    // 保存用户行为数据
    public UserBehavior save (UserBehavior behavior) {
        return behaviorRepository.save(behavior);
    }

    // 批量保存(适合批量导入数据)
    public List batchSave(List behaviors) {
        return behaviorRepository.saveAll(behaviors);
    }

    // 查询指定日期范围的行为数据
    public List getBehaviorByDateRange(LocalDateTime start, LocalDateTime end) {
        return behaviorRepository.findByCreateTimeBetween(start, end);
    }

    // 统计分类商品点击量(实时分析核心接口)
    public Long getCategoryClickCount(Integer categoryId, LocalDateTime start, LocalDateTime end) {
        return behaviorRepository.countClickByCategoryId(categoryId, start, end);
    }
}
4、接口层(Controller)

提供 HTTP 接口供前端调用:

package com.example.dorisdemo.controller;

import com.example.dorisdemo.entity.UserBehavior;
import com.example.dorisdemo.service.UserBehaviorService;
import lombok.RequiredArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;

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

@RestController
@RequestMapping("/user-behavior")
@RequiredArgsConstructor
publicclass UserBehaviorController {

    privatefinal UserBehaviorService behaviorService;

    // 保存单个行为数据
    @PostMapping
    public UserBehavior save(@RequestBody UserBehavior behavior) {
        return behaviorService.save(behavior);
    }

    // 批量保存行为数据
    @PostMapping("/batch")
    public List batchSave(@RequestBody List behaviors) {
        return behaviorService.batchSave(behaviors);
    }

    // 按日期范围查询
    @GetMapping("/range")
    public List getByRange(
            @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
 LocalDateTime start,
            @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime end) 
{
        return behaviorService.getBehaviorByDateRange(start, end);
    }

    // 统计分类点击量(实时分析接口)
    @GetMapping("/click-count")
    public Long getClickCount(
            @RequestParam Integer categoryId,
            @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
 LocalDateTime start,
            @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime end) 
{
        return behaviorService.getCategoryClickCount(categoryId, start, end);
    }
}

第五步:测试与验证

1、 启动项目

运行DorisDemoApplication.java,确保项目启动成功,无报错。

2、 接口测试(用 Postman 或 curl)

批量插入测试数据:

POST /user-behavior/batch
Content-Type: application/json
[
    {"userId":1001,"productId":2001,"categoryId":301,"behaviorType":"click","createTime":"2024-01-15 10:30:00"},
    {"userId":1002,"productId":2002,"categoryId":301,"behaviorType":"click", "createTime":"2024-01-15 11:20:00"},
    {"userId":1003,"productId":2003,"categoryId":302,"behaviorType":"purchase","createTime":"2024-01-15 14:10:00"}
]

统计 301 分类的点击量:

GET /user-behavior/click-count?categoryId=301&start=2024-01-01 00:00:00&end=2024-01-31 23:59:59
3、性能对比测试

用 100 万条用户行为数据进行测试,对比 MySQL 和 Doris 的查询性能:

可以看到,Doris 在分析场景下的性能远超 MySQL,且数据量越大,优势越明显。

小结

Spring Boot 与 Apache Doris 的集成极其简单(兼容 MySQL 协议和 JDBC 驱动),但带来的性能提升却是革命性的 —— 它让开发者无需关注复杂的分布式架构,就能快速实现 PB 级数据的实时分析能力,完美替代 MySQL 在 OLAP 场景中的不足。

未来,随着实时数据需求的进一步增长,Apache Doris 这类专注于分析场景的数据库将成为技术栈的标配。无论是电商、金融、物流还是政务领域,只要涉及大数据量的实时统计、报表生成、多维度分析,“Spring Boot + Apache Doris” 都是值得优先选择的方案。

如果你还在为 MySQL 的分析性能瓶颈烦恼,不妨试试这个组合,相信会给你带来惊喜!欢迎在评论区分享你的使用体验或疑问~

构建高质量的技术交流社群,欢迎从事编程开发、技术招聘HR进群,也欢迎大家分享自己公司的内推信息,相互帮助,一起进步!

文明发言,以交流技术职位内推行业探讨为主

广告人士勿入,切勿轻信私聊,防止被骗

图片
加我好友,拉你进群

点下方的“❤ 支持我们,非常感谢!

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/190118