基于 DDD 的互联网“赞&踩”系统

开发 架构
赞/踩服务是一种用户反馈机制。随着社交媒体的普及和发展,人们越来越喜欢在一种平台上分享自己的观点和生活,这时就需要一种形式化的反馈机制来快速评价这些信息的好坏。赞/踩服务的目标是为了提高用户互动性,增加内容的社会影响力,从而增加活跃用户数量。

该文是系统的用户手册,主要体现系统功能和所支持的高级特性,关于系统的核心设计正在整理中,请稍安勿躁

1. 背景

随着社交媒体的普及,用户生成的内容数量急剧增加。为了帮助用户更好地发现和分享内容,许多社交媒体平台都提供了赞/踩服务。

2. 目标

赞/踩服务是一种用户反馈机制。随着社交媒体的普及和发展,人们越来越喜欢在一种平台上分享自己的观点和生活,这时就需要一种形式化的反馈机制来快速评价这些信息的好坏。赞/踩服务的目标是为了提高用户互动性,增加内容的社会影响力,从而增加活跃用户数量。

3. 快速入门

系统所涉及的功能包括:

功能

描述

赞/踩

用户可以点击对应的赞或踩按钮,以表达自己的喜好或不喜好

取消赞/踩

用户可以取消之前的赞/踩,以更正自己的想法

计数器

赞和踩的数量都需要计数器,用于显示文章或评论的受欢迎程度和社交影响力等

赞/踩历史

用户可以查看自己赞/踩的历史记录,以查看自己对文章或评论的态度

3.1. 开发环境

基于 Spring Boot 框架进行开发,以 DDD 作为业务逻辑承载模型。

框架

版本

依赖说明

JDK

1.8+

运行环境

Spring Boot

2.3.12.RELEASE


Spring Data

2.3.9.RELEASE

基于 JPA 实现持久化;基于 Redis 完成缓存加速(可选)

Lego

0.1.22

DDD 模型落地

springfox

3.0.0

文档管理

RocketMQ

2.2.1

领域事件,异步处理(可选)

Sharding Sphere

4.4.1

分库分表(可选)

3.2. 模块介绍

该项目使用标准的 “六边形架构”,对业务和技术进行分离,所以模块较多,但层次更为清晰。

模块

作用

domain

核心逻辑层,DDD 中核心组件,包括实体、值对象、聚合根、领域服务等

app

应用服务层,DDD 中的应用服务,主要负责流程编排

infrastructure

基础设施层,主要负责与 DB 或其他服务进行通讯

api

RPC 服务中的接口定义,被 FeignClient 和 FeignService 依赖

FeignService

api 中接口的实际实现者,完成接口的适配

FeignClient

api 中Proxy实现者,方便使用方直接调用

bootstrap

应用启动入口,包括 Spring Boot 入口和所有配置

3.3. 启动项目

3.3.1. 建库建表

建表语句在infrastructure/src/main/resources/sql 目标下,包括单库和分库分表配置。单库建表语句如下:

create table dislike_action
(
    id          bigint auto_increment primary key,
    create_time datetime    not null,
    delete_time datetime    null,
    update_time datetime    null,
    vsn         int         not null,
    status      char(16)    not null,
    target_id   bigint      not null,
    target_type varchar(16) not null,
    user_id     bigint      not null,
    constraint unq_user_target
        unique (user_id, target_type, target_id)
);
create table dislike_target_count
(
    id          bigint auto_increment primary key,
    create_time datetime    not null,
    delete_time datetime    null,
    update_time datetime    null,
    vsn         int         not null,
    count       bigint      not null,
    target_id   bigint      not null,
    target_type varchar(16) not null,
    constraint unq_target
        unique (target_id, target_type)
);
create table like_action
(
    id          bigint auto_increment primary key,
    create_time datetime    not null,
    delete_time datetime    null,
    update_time datetime    null,
    vsn         int         not null,
    status      char(16)    not null,
    target_id   bigint      not null,
    target_type varchar(16) not null,
    user_id     bigint      not null,
    constraint unq_user_target
        unique (user_id, target_type, target_id)
);
create table like_target_count
(
    id          bigint auto_increment primary key,
    create_time datetime    not null,
    delete_time datetime    null,
    update_time datetime    null,
    vsn         int         not null,
    count       bigint      not null,
    target_id   bigint      not null,
    target_type varchar(16) not null,
    constraint unq_target
        unique (target_id, target_type)
);

3.3.2. 修改数据库配置

修改bootstrap/src/main/resource/application.yml 增加数据配置,具体如下:

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/like
    username: root
    password: root

3.3.3. 启动应用程序

直接运行 bootstrap 模块下的 LikeApplication 类,输入地址:http://127.0.0.1:8080/swagger-ui/

当看到如下界面证明程序启动成功:

3.3. 核心 API

核心接口如下:

功能

请求地址

参数类型

参数说明

返回结果

cur Demo

点赞

POST /feignService/action/command/like

RequestBody

{"userId": 用户id, "targetType": 目标对象类型,"targetId": 目标对象 id}

curl -X POST "http://127.0.0.1:8080/feignService/action/command/like" -H "accept: /" -H "Content-Type: application/json" -d "{"targetId":1,"targetType":"TEST","userId":2}"

取消点赞

POST /feignService/action/command/unlike

RequestBody

{"userId": 用户id, "targetType": 目标对象类型,"targetId": 目标对象 id}

curl -X POST "http://127.0.0.1:8080/feignService/action/command/unlike" -H "accept: /" -H "Content-Type: application/json" -d "{"targetId":1,"targetType":"test","userId":2}"

获取点赞数量

GET /feignService/targetCount/query/getLikeCountByTarget

RequestParam

type:目标类型;ids:目标id集合

[{"targetType":目标对象类型,“targetId":目标对象id,"count":点赞数量}]

curl -X GET "http://127.0.0.1:8080/feignService/targetCount/query/getLikeCountByTarget?type=test&ids=1" -H "accept: /"

获取点赞记录

GET /feignService/action/query/getLikeByUserAndType

RequestParam

type:目标类型;userId:userId

[{"targetType":目标对象类型,“targetId":目标对象id,"userId":用户id,"valid":是否有效}]

curl -X GET "http://127.0.0.1:8080/feignService/action/query/getLikeByUserAndType?userId=2&type=test" -H "accept: /"

POST /feignService/action/command/dislike

RequestBody

{"userId": 用户id, "targetType": 目标对象类型,"targetId": 目标对象 id}

curl -X POST "http://127.0.0.1:8080/feignService/action/command/dislike" -H "accept: /" -H "Content-Type: application/json" -d "{"targetId":1,"targetType":"test","userId":2}"

取消踩

POST /feignService/action/command/unDislike

RequestBody

{"userId": 用户id, "targetType": 目标对象类型,"targetId": 目标对象 id}

curl -X POST "http://127.0.0.1:8080/feignService/action/command/unDislike" -H "accept: /" -H "Content-Type: application/json" -d "{"targetId":1,"targetType":"test","userId":2}"

获取踩数量

GET /feignService/targetCount/query/getDislikeCountByType

RequestParam

type:目标类型;ids:目标id集合

[{"targetType":目标对象类型,“targetId":目标对象id,"count":点赞数量}]

curl -X GET "http://127.0.0.1:8080/feignService/targetCount/query/getDislikeCountByType?type=test&ids=1" -H "accept: /"

获取点赞记录

GET /feignService/action/query/getDislikeByUserAndType

RequestParam

type:目标类型;userId:userId

[{"targetType":目标对象类型,“targetId":目标对象id,"userId":用户id,"valid":是否有效}]

curl -X GET "http://127.0.0.1:8080/feignService/action/query/getDislikeByUserAndType?userId=2&type=test" -H "accept: /"

核心API直接在 Swagger UI 上进行测试即可!!!

4. 高级特性

4.1. 自定义业务验证

流程中涉及两个重要的概念:

  • ActionUser: 操作 赞或踩 的用户;
  • ActionTarget: 赞或踩 的目的对象;

在实际业务场景,需要对这两个对象的有效性进行验证,比如:

  • ActionUser

用户是否存在?

用户状态是否有效?

是否是黑名单用户?

  • ActionTarget
  • 目标对象是否存在?
  • 目标对象是否已经下线/禁用?

这些功能扩展直接实现对应的 Loader 即可。

4.1.1. ActionUser 扩展

ActionUser 定义如下:

public class ActionUser {
    @Column(name = "user_id", updatable = false)
    private Long userId;
    @Transient
    private boolean valid;
}

如果用户状态存在问题,直接将 valid 置为 false 即可。

ActionUserLoader 定义如下:

public interface ActionUserLoader {
    ActionUser loadByUserId(Long userId);
}

只需实现 ActionUserLoader 并注册为 Spring 托管 Bean 即可,具体如下:

@Component(value = LoadActionUserByUserId.BEAN_NAME)
public class TestActionUserLoader implements ActionUserLoader {
    @Override
    public ActionUser loadByUserId(Long userId) {
        if (userId == null || userId.longValue() < 0){
            return ActionUser.apply(userId, false);
        }else {
            return ActionUser.apply(userId);
        }
    }
}

当 userId 为 null 或者 小于 0 时,表明为无效用户,将 valid 设置为 false。

【备注】Bean 必须注册为LoadActionUserByUserId.BEAN_NAME(actionUserLoader),否则框架将无法识别。

4.1.2. ActionTarget 扩展

ActionTarget 定义如下:

public class ActionTarget {
    @Column(name = "target_type", updatable = false)
    private String type;
    @Column(name = "target_id", updatable = false)
    private Long id;
    @Transient
    private boolean valid;
}

如果目标对象状态存在问题,直接将 valid 置为 false 即可。

由于系统中可以存在多种目标对象,为每个类型提供单独的 Loader,接口如下:

public interface SingleActionTargetLoader {
    /**
     * 是否支持 type 类型的 Target
     * @param type
     * @return
     */
    boolean support(String type);
    /**
     * 加载 Target 对象
     * @param type
     * @param id
     * @return
     */
    ActionTarget load(String type, Long id);
}

按需要实现接口,样例如下:

@Component
@Order(0)
public class TestActionTargetLoader
        extends AbstractSingleActionTargetLoader
        implements SingleActionTargetLoader {
    public TestActionTargetLoader() {
        super("Test");
    }
    @Override
    protected ActionTarget doLoadById(String type, Long id) {
        if (id == null || id.longValue() < 0){
            return ActionTarget.apply(type, id, false);
        }else {
            return ActionTarget.apply(type, id);
        }
    }
}

该实现对 type 为 Test 的 Target 进行加载。

4.2. 发布领域事件

领域事件是 DDD 中的重要概念,当系统发生状态变化后,将变化结果对外进行广播,从而实现系统间的集成。

4.2.1. 添加 RocketMQ

外部领域事件通过 RocketMQ 向外广播,需要搭建 RocketMQ 集群并在项目中增加 RocketMQ 的支持。

在 bootstrap 模块的 pom 中增加 rocketmq starter,具体如下:

<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-spring-boot-starter</artifactId>
</dependency>

在 application.yml 增加 rocketmq 的配置,具体如下:

rocketmq:
  name-server: http://127.0.0.1:9876
  producer:
    group: like-service

至此,便完成了与 rocketmq 的集成。

4.2.2. 打开领域事件开关

在 application.yml 添加如下配置:

like:
  event:
    #开启领域事件
    enable: true
    #指定领域事件发送的 topic
    topic: like-event-topic

开启领域事件,并指定事件发送的 topic

4.2.3. 测试领域事件

重新启动项目,当控制台输出以下表明配置成功:

Use RocketMQ to Publish Like Event

使用 swagger 运行 dislike 操作,从日志中可知消息发送成功:

4.2.4. 支持领域事件

系统支持的领域事件包括:

领域事件类型

触发机制

tag

消息体

LikeMarkedEvent

点赞成功

LikeMarkedEvent

见 LikeMarkedEvent 类

LikeCancelledEvent

取消点赞成功

LikeCancelledEvent

见 LikeCancelledEvent 类

DislikeMarkedEvent

踩成功

DislikeMarkedEvent

见 DislikeMarkedEvent 类

DislikeCancelledEvent

取消踩成功

DislikeCancelledEvent

见 DislikeCancelledEvent 类

4.3. 缓存加速

在系统中,获取目标对象的 赞/踩 数量接口调用量最大,会成为系统的第一个性能卡点,针对这个问题,可以通过引入 redis 缓存进行性能加速。

4.3.1. 添加 redis 依赖

首先需要引入 redis 相关依赖,在 bootstrap 的 pom 中增加如下配置:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

然后在 application.yml 中增加 redis 相关配置:

spring:
  redis:
    host: 127.0.0.1
    port: 6379

4.3.2. 开启缓存

完成redis配置后,需要在 application.yml 开启对应的缓存,具体如下:

target:
  count:
    dislike:
      cache:
        # 是否开启缓存
        enable: true
    like:
      cache:
        # 是否开启缓存
        enable: true

4.3.3. 缓存效果

未开启缓存前,每次查询数量都会执行一条 sql,具体如下:curl 命令如下:

curl -X GET "http://127.0.0.1:8080/feignService/targetCount/query/getDislikeCountByType?type=test&ids=1" -H "accept: */*"

输出 sql 如下:

Hibernate: select disliketar0_.id as id1_1_, disliketar0_.create_time as create_t2_1_, disliketar0_.delete_time as delete_t3_1_, disliketar0_.update_time as update_t4_1_, disliketar0_.vsn as vsn5_1_, disliketar0_.count as count6_1_, disliketar0_.target_id as target_i7_1_, disliketar0_.target_type as target_t8_1_ from dislike_target_count disliketar0_ where disliketar0_.target_type=? and (disliketar0_.target_id in (?))

开启缓存后,再次执行以上 curl,控制台不会输出sql,而是会输出一行日志:

c.g.l.i.s.RedisBasedTargetCountCache     : load All Data From Cache for test and [1]

说明缓存已经生效。

所有的 action 操作都会与同步的对缓存进行更新。

4.4. 异步存储

当目标对象出现热点时,会产生高并发请求,对于 action 来说,主要以数据插入和数据的分散更新为主。但对于 count,就会产生热点更新,从而成为系统的瓶颈。在这个场景,最适合的解决方案便是引入 MQ 对流量进行削峰填谷。

4.4.1. 增加 rocketmq

与 4.2.1. 添加 RocketMQ 内容一致,在此不再重复。

4.4.2. 开启异步化

在 application.yml 中增加如下配置:

target:
  count:
    dislike:
      async:
        # 是否开启异步更新
        enable: true
        # 异步更新所使用的 topic
        topic: dislike-target-count-async-topic
        # 异步更新使用的消费者组
        consumerGroup: dislike-target-count-async-group
    like:
      async:
        # 是否开启异步更新
        enable: true
        # 异步更新所使用的 topic
        topic: like-target-count-async-topic
        # 异步更新使用的消费者组
        consumerGroup: like-target-count-async-group

4.4.3. 异步效果

重启应用程序,在 swagger 中执行 点赞 操作,从控制台可以看到如下日志:

[nio-8080-exec-7] c.g.l.c.a.order.OrderedAsyncInterceptor  : success to send orderly async Task to RocketMQ, args is [ActionTarget(type=test, id=18, valid=true), 1], shardingKey is 18, msg is GenericMessage [payload={"0":"{\"type\":\"test\",\"id\":18,\"valid\":true}","1":"1"}, headers={id=c84e6be5-acec-27c2-3f44-6250003a56c7, timestamp=1685275901638}], result is SendResult [sendStatus=SEND_OK, msgId=7F0000014F505C8DA9628F610AC60007, offsetMsgId=C0A8032300002A9F00000000001A0AFD, messageQueue=MessageQueue [topic=dislike-target-count-async-topic, brokerName=MacdeMacBook-Pro-171.local, queueId=3], queueOffset=8]
[MessageThread_4] g.l.i.d.DislikeTargetCountRepositoryImpl : begin to incr for db target ActionTarget(type=test, id=18, valid=true), count 1
[nio-8080-exec-7] com.geekhalo.like.app.RocketMQPublisher  : success to send msg GenericMessage [payload={"targetId":18,"targetType":"test","userId":1}, headers={id=4e8e13f9-b3cd-7b90-059f-f506f09d9948, timestamp=1685275901640}] to like-event-topic:DislikeMarkedEvent, msgId is 7F0000014F505C8DA9628F610AC80008
[nio-8080-exec-7] c.g.l.c.c.s.AbstractCommandService       : success to sync AbstractCommandService.Syncer.Data(id=106, action=UPDATE, a=DislikeAction(super=AbstractAction(super=AbstractAggRoot(super=AbstractEntity(vsn=0, createAt=Sun May 28 20:11:41 CST 2023, updateAt=Sun May 28 20:11:41 CST 2023, deleteAt=null), events=[]), id=106, user=ActionUser(userId=1, valid=true), target=ActionTarget(type=test, id=18, valid=true), status=VALID)))
[MessageThread_4] g.l.i.d.DislikeTargetCountRepositoryImpl : success to incr for db target ActionTarget(type=test, id=18, valid=true), count 1
[MessageThread_4] .s.AbstractSingleMethodConsumerContainer : consume message 7F0000014F505C8DA9628F610AC60007, cost: 27 ms

从日志上看,可以得出:

  • nio 线程向 MQ 发送消息
  • MessageThread 线程从 MQ 中获取数据并执行 incr 操作

4.5. 分库分表

随着系统的运行,数据量会逐渐增大,最终超出单个 DB 的容量上限。这种情况下,最佳实践便是对数据库进行分库分表。

4.5.1. 构建数据库和表

在 infrastructure 模块的 sql 目录下存在两个 sql 文件:

  • create_table_sharding_action.sql : 赞/踩 操作分库分表
  • create_table_sharding_count.sql : 赞/踩 计数分库分表

示例中总共分16张表,存放在两个数据库中:

  • db1 存放 0-7 表
  • db2 存放 8-15 表

如图所示:

4.5.2. 添加 ShardingSphere 支持

在 bootstrap 的pom 文件增加 ShardingSphere 的依赖,具体如下:

<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
</dependency>

其次,增加分库分表配置文件,为了方便新建 application.properties 存放分库分表配置:

# 数据源配置
# 总共4个数据源
spring.shardingsphere.datasource.names=action-ds0, action-ds1, count-ds0, count-ds1 
# action-ds0 数据源配置
spring.shardingsphere.datasource.action-ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.action-ds0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.action-ds0.jdbc-url=jdbc:mysql://127.0.0.1:3306/like_action_0?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC
spring.shardingsphere.datasource.action-ds0.username=root
spring.shardingsphere.datasource.action-ds0.password=root
# action-ds1 数据源配置
spring.shardingsphere.datasource.action-ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.action-ds1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.action-ds1.jdbc-url=jdbc:mysql://127.0.0.1:3306/like_action_1?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC
spring.shardingsphere.datasource.action-ds1.username=root
spring.shardingsphere.datasource.action-ds1.password=root
# count-ds0 数据源配置
spring.shardingsphere.datasource.count-ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.count-ds0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.count-ds0.jdbc-url=jdbc:mysql://127.0.0.1:3306/like_count_0?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC
spring.shardingsphere.datasource.count-ds0.username=root
spring.shardingsphere.datasource.count-ds0.password=root
# count-ds1 数据源配置
spring.shardingsphere.datasource.count-ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.count-ds1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.count-ds1.jdbc-url=jdbc:mysql://127.0.0.1:3306/like_count_1?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC
spring.shardingsphere.datasource.count-ds1.username=root
spring.shardingsphere.datasource.count-ds1.password=root
# 分库分表规则配置
# 使用雪花算法生成分布式主键id的值
spring.shardingsphere.sharding.default-key-generator.column=id
spring.shardingsphere.sharding.default-key-generator.column-type=BIGINT
spring.shardingsphere.sharding.default-key-generator.type=SNOWFLAKE
spring.shardingsphere.sharding.default-key-generator.algorithm-expression=SNOWFLAKE_HASH(id, 12)
spring.shardingsphere.sharding.default-key-generator.matrix-handling-type=SHARDING_DEFAULT
# 踩行为表配置
spring.shardingsphere.sharding.tables.dislike_action.actual-data-nodes=action-ds0.dislike_action_$->{0..7},action-ds1.dislike_action_$->{8..15}
# user_id 为分表分片键
spring.shardingsphere.sharding.tables.dislike_action.table-strategy.inline.sharding-column=user_id
# 根据 user_id 以 16 取模,进行分表
spring.shardingsphere.sharding.tables.dislike_action.table-strategy.inline.algorithm-expression=dislike_action_$->{Math.abs(user_id.hashCode())  % 16}
# user_id 为分库分片键
spring.shardingsphere.sharding.tables.dislike_action.database-strategy.inline.sharding-column=user_id
# 根据 user_id 以 16 取模后除8 ,进行分库
spring.shardingsphere.sharding.tables.dislike_action.database-strategy.inline.algorithm-expression=action-ds$->{Math.floorDiv((Math.abs(user_id.hashCode())  % 16) , 8)}
spring.shardingsphere.sharding.tables.like_action.actual-data-nodes=action-ds0.like_action_$->{0..7},action-ds1.like_action_$->{8..15}
spring.shardingsphere.sharding.tables.like_action.table-strategy.inline.sharding-column=user_id
spring.shardingsphere.sharding.tables.like_action.table-strategy.inline.algorithm-expression=like_action_$->{Math.abs(user_id.hashCode())  % 16}
spring.shardingsphere.sharding.tables.like_action.database-strategy.inline.sharding-column=user_id
spring.shardingsphere.sharding.tables.like_action.database-strategy.inline.algorithm-expression=action-ds$->{Math.floorDiv((Math.abs(user_id.hashCode())  % 16) , 8)}
# 计数表配置
spring.shardingsphere.sharding.tables.dislike_target_count.actual-data-nodes=count-ds0.dislike_target_count_$->{0..7},count-ds1.dislike_target_count_$->{8..15}
# target_id 为分表分片键
spring.shardingsphere.sharding.tables.dislike_target_count.table-strategy.inline.sharding-column=target_id
# 根据 target_id 以 16 取模,进行分表
spring.shardingsphere.sharding.tables.dislike_target_count.table-strategy.inline.algorithm-expression=dislike_target_count_$->{Math.abs(target_id.hashCode())  % 16}
# target_id 为分库分片键
spring.shardingsphere.sharding.tables.dislike_target_count.database-strategy.inline.sharding-column=target_id
# 根据 target_id 以 16 取模后除8 ,进行分库
spring.shardingsphere.sharding.tables.dislike_target_count.database-strategy.inline.algorithm-expression=count-ds$->{Math.floorDiv((Math.abs(target_id.hashCode()) % 16), 8)}
spring.shardingsphere.sharding.tables.like_target_count.actual-data-nodes=count-ds0.like_target_count_$->{0..7},count-ds1.like_target_count_$->{8..15}
spring.shardingsphere.sharding.tables.like_target_count.table-strategy.inline.sharding-column=target_id
spring.shardingsphere.sharding.tables.like_target_count.table-strategy.inline.algorithm-expression=like_target_count_$->{Math.abs(target_id.hashCode()) % 16}
spring.shardingsphere.sharding.tables.like_target_count.database-strategy.inline.sharding-column=target_id
spring.shardingsphere.sharding.tables.like_target_count.database-strategy.inline.algorithm-expression=count-ds$->{Math.floorDiv((Math.abs(target_id.hashCode()) % 16), 8)}
# 打印 SQL 配置(可选)
spring.shardingsphere.props.sql.show=true

在雪花算法情况下,尾数会变的极度不均匀,所以在进行计算之前,通常先执行 hashCode 在进行取模操作。

4.5.3. 分库分表效果

启动应用程序,控制台输出 sharding 相关配置,具体如下:

defaultKeyGenerator:
  column: id
  type: SNOWFLAKE
tables:
  dislike_action:
    actualDataNodes: action-ds0.dislike_action_$->{0..7},action-ds1.dislike_action_$->{8..15}
    databaseStrategy:
      inline:
        algorithmExpression: action-ds$->{Math.floorDiv((Math.abs(user_id.hashCode())  % 16) , 8)}
        shardingColumn: user_id
    logicTable: dislike_action
    tableStrategy:
      inline:
        algorithmExpression: dislike_action_$->{Math.abs(user_id.hashCode()) % 16}
        shardingColumn: user_id
  like_action:
    actualDataNodes: action-ds0.like_action_$->{0..7},action-ds1.like_action_$->{8..15}
    databaseStrategy:
      inline:
        algorithmExpression: action-ds$->{Math.floorDiv((Math.abs(user_id.hashCode())  % 16) , 8)}
        shardingColumn: user_id
    logicTable: like_action
    tableStrategy:
      inline:
        algorithmExpression: like_action_$->{Math.abs(user_id.hashCode())  % 16}
        shardingColumn: user_id
  dislike_target_count:
    actualDataNodes: count-ds0.dislike_target_count_$->{0..7},count-ds1.dislike_target_count_$->{8..15}
    databaseStrategy:
      inline:
        algorithmExpression: count-ds$->{Math.floorDiv((Math.abs(target_id.hashCode()) % 16), 8)}
        shardingColumn: target_id
    logicTable: dislike_target_count
    tableStrategy:
      inline:
        algorithmExpression: dislike_target_count_$->{Math.abs(target_id.hashCode())  % 16}
        shardingColumn: target_id
  like_target_count:
    actualDataNodes: count-ds0.like_target_count_$->{0..7},count-ds1.like_target_count_$->{8..15}
    databaseStrategy:
      inline:
        algorithmExpression: count-ds$->{Math.floorDiv((Math.abs(target_id.hashCode()) % 16), 8)}
        shardingColumn: target_id
    logicTable: like_target_count
    tableStrategy:
      inline:
        algorithmExpression: like_target_count_$->{Math.abs(target_id.hashCode()) % 16}
        shardingColumn: target_id

在 Swagger UI 中操作点赞,控制台输出如下:

Logic SQL: select dislikeact0_.id as id1_0_, dislikeact0_.create_time as create_t2_0_, dislikeact0_.delete_time as delete_t3_0_, dislikeact0_.update_time as update_t4_0_, dislikeact0_.vsn as vsn5_0_, dislikeact0_.status as status6_0_, dislikeact0_.target_id as target_i7_0_, dislikeact0_.target_type as target_t8_0_, dislikeact0_.user_id as user_id9_0_ from dislike_action dislikeact0_ where dislikeact0_.user_id=? and dislikeact0_.target_type=?
Actual SQL: action-ds0 ::: select dislikeact0_.id as id1_0_, dislikeact0_.create_time as create_t2_0_, dislikeact0_.delete_time as delete_t3_0_, dislikeact0_.update_time as update_t4_0_, dislikeact0_.vsn as vsn5_0_, dislikeact0_.status as status6_0_, dislikeact0_.target_id as target_i7_0_, dislikeact0_.target_type as target_t8_0_, dislikeact0_.user_id as user_id9_0_ from dislike_action_0 dislikeact0_ where dislikeact0_.user_id=? and dislikeact0_.target_type=? ::: [2707692781417059328, Test]

其中:

  • Logic SQL:逻辑 SQL 中的表为 dislike_action
  • Actual SQL:实际执行的 SQL 表为 dislike_action_0,数据库为 action-ds0

5. 项目信息

项目地址见:https://gitee.com/litao851025/lego/tree/master/services/like

责任编辑:武晓燕 来源: 今日头条
相关推荐

2022-06-09 08:01:43

秒杀系统互联网架构

2015-05-28 16:11:07

互联网+

2015-06-24 15:35:54

2018-08-15 09:02:59

产业互联网工业互联网物联网

2020-04-17 14:37:09

大数据工业互联网技术

2017-08-03 16:37:35

互联网法院司法

2015-10-08 15:20:34

互联网物联网

2014-08-12 14:01:19

SDNICNCDN

2014-01-15 14:35:35

云计算

2019-04-04 15:01:03

2015-10-30 17:50:18

互联网金融

2012-06-26 13:18:23

互联网公社

2011-08-19 11:33:32

2015-09-22 09:17:33

互联网发展史

2016-12-02 15:23:42

锐捷互联网技术核心交换机

2023-07-13 15:26:27

无人机互联网

2013-01-25 10:31:02

互联网公司移动互联网船票

2019-12-11 15:05:27

互联网IT云计算

2009-07-02 09:30:00

思科互联网Office

2019-04-10 21:33:34

点赞
收藏

51CTO技术栈公众号