实战篇之基于二进制思想的用户标签系统(Mysql+SpringBoot2

简介: 实战篇之基于二进制思想的用户标签系统(Mysql+SpringBoot2

四:一起动手实现用户标签系统 - 底层标签读写组件的实现
4.1: 建立用户标签表SQL
``CREATE TABLEt_user_tag(user_idbigint NOT NULL DEFAULT -1 COMMENT '用户 id',tag_info_01bigint NOT NULL DEFAULT '0' COMMENT '标签记录字段',tag_info_02bigint NOT NULL DEFAULT '0' COMMENT '标签记录字段',tag_info_03bigint NOT NULL DEFAULT '0' COMMENT '标签记录字段',create_timedatetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时 间',update_timedatetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin
COMMENT='用户标签记录';

  ###4.2:service层接口
  ```package com.laoyang.provider.service;

import com.laoyang.constants.UserTagsEnum;

/**
 * @author:Kevin
 * @create: 2023-08-01 09:53
 * @Description:
 */

public interface IUserTagService {

    /**
     * 设置标签 只能设置成功一次
     *
     * @param userId
     * @param userTagsEnum
     * @return
     */
    boolean setTag(Long userId, UserTagsEnum userTagsEnum);
    /**
     * 取消标签
     *
     * @param userId
     * @param userTagsEnum
     * @return
     */
    boolean cancelTag(Long userId, UserTagsEnum userTagsEnum);
    /**
     * 是否包含某个标签
     *
     * @param userId
     * @param userTagsEnum
     * @return
     */
    boolean containTag(Long userId,UserTagsEnum userTagsEnum);
}

```package com.laoyang.provider.service.impl;

import com.laoyang.common.utils.ConvertBeanUtils;
import com.laoyang.constants.UserTagFieldNameConstants;
import com.laoyang.constants.UserTagsEnum;
import com.laoyang.dto.UserTagDTO;
import com.laoyang.framework.redis.key.UserProviderCacheKeyBuilder;
import com.laoyang.provider.dao.mapper.IUserTagMapper;
import com.laoyang.provider.dao.po.UserTagPO;
import com.laoyang.provider.service.IUserTagService;
import com.laoyang.usils.TagInfoUtils;
import jakarta.annotation.Resource;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;

import java.nio.charset.StandardCharsets;

/**

  • @author:Kevin
  • @create: 2023-08-01 09:54
  • @Description:
    */

public class UserTagServiceImpl implements IUserTagService {

@Resource
private IUserTagMapper userTagMapper;
@Resource
private RedisTemplate<String, String> redisTemplate;

private RedisTemplate<String, UserTagDTO> userTagDTORedisTemplate;

@Resource
private UserProviderCacheKeyBuilder cacheKeyBuilder;

@Override
public boolean setTag(Long userId, UserTagsEnum userTagsEnum) {
    boolean updateStatus = userTagMapper.setTag(userId,userTagsEnum.getFieldName(),userTagsEnum.getTag()) > 0;
    if (updateStatus){
        String redisKey = cacheKeyBuilder.buildtagInfoKey(userId);
        userTagDTORedisTemplate.delete(redisKey);
        return  true;
    }
    String key = cacheKeyBuilder.buildTagLockKey(userId);
    //TODO 分布式锁 实现多个线程之间对同一个资源的互斥访问,保证同一时间只有一个线程能够获取到锁并执行相应的操作
    String setNxResult = redisTemplate.execute(new RedisCallback<String>() {
        @Override
        public String doInRedis(RedisConnection connection) throws DataAccessException {
            RedisSerializer keySerializer = redisTemplate.getKeySerializer();
            RedisSerializer valueSerializer = redisTemplate.getValueSerializer();
            String result = (String) connection.execute("set", keySerializer.serialize(key),
                    valueSerializer.serialize("-1"),
                    "NX".getBytes(StandardCharsets.UTF_8),
                    "EX".getBytes(StandardCharsets.UTF_8),
                    "3".getBytes(StandardCharsets.UTF_8));
            return result;
        }
    });

    if (!"OK".equals(setNxResult)){
        return false;
    }

    UserTagPO userTagPO = userTagMapper.selectById(userId);
    if (userTagPO!=null){
        return false;
    }
    userTagPO = new UserTagPO();
    userTagPO.setUserId(userId);
    userTagMapper.insert(userTagPO);
    updateStatus = userTagMapper.setTag(userId,userTagsEnum.getFieldName(),userTagsEnum.getTag()) > 0;
    redisTemplate.delete(key);
    return updateStatus;
}

@Override
public boolean cancelTag(Long userId, UserTagsEnum userTagsEnum) {
    boolean cancleStatus = userTagMapper.cancelTag(userId,userTagsEnum.getFieldName(),userTagsEnum.getTag()) > 0;
    if (cancleStatus){
        return false;
    }
    String redisKey = cacheKeyBuilder.buildtagInfoKey(userId);
    userTagDTORedisTemplate.delete(redisKey);
    return true;
}

@Override
public boolean containTag(Long userId, UserTagsEnum userTagsEnum) {
    UserTagDTO userTagDTO = this.queryByUserId(userId);
    if (userTagDTO == null) {
        return false;
    }
    String queryFieldName = userTagsEnum.getFieldName();
    if
    (UserTagFieldNameConstants.TAG_INFO_01.equals(queryFieldName)) {
        return
                TagInfoUtils.isContain(userTagDTO.getTagInfo01(),
                        userTagsEnum.getTag());
    } else if
    (UserTagFieldNameConstants.TAG_INFO_02.equals(queryFieldName)) {
        return
                TagInfoUtils.isContain(userTagDTO.getTagInfo02(),
                        userTagsEnum.getTag());
    } else if
    (UserTagFieldNameConstants.TAG_INFO_03.equals(queryFieldName)) {
        return
                TagInfoUtils.isContain(userTagDTO.getTagInfo03(),
                        userTagsEnum.getTag());
    }
    return false;
}

/**
 * 从redis查询用户标签
 * @param userId
 * @return
 */
private UserTagDTO queryByUserId(Long userId){
    String redisKey = cacheKeyBuilder.buildtagInfoKey(userId);
    UserTagDTO userTagDTO = userTagDTORedisTemplate.opsForValue().get(redisKey);
    if (userTagDTO != null){
        return userTagDTO;
    }
    UserTagPO userTagPO = userTagMapper.selectById(userId);
    if (userTagPO == null){
        return null;
    }
    userTagDTO = ConvertBeanUtils.convert(userTagPO,UserTagDTO.class);
    userTagDTORedisTemplate.opsForValue().set(redisKey, userTagDTO);

    return userTagDTO;

}

}
```
说明:我们使用了redis作为缓存,mybatisplus, 并自行创建了redis业务主键生成工具类等等,会放在最后,先把核心代码呈现。这里说明下使用到了redis分布式实现

这段代码是使用RedisTemplate执行一个"set"命令,并设置了一些选项参数。下面对代码进行解释:

首先,通过redisTemplate.getKeySerializer()获取key的序列化器,通过redisTemplate.getValueSerializer()获取value的序列化器。
在RedisCallback的doInRedis方法中,通过RedisConnection的execute方法执行"set"命令。
参数中,keySerializer.serialize(key)将key序列化为字节数组,valueSerializer.serialize("-1")将value序列化为字节数组。
"NX".getBytes(StandardCharsets.UTF_8)表示设置NX选项,即只有在key不存在时才进行set操作。
"EX".getBytes(StandardCharsets.UTF_8)表示设置EX选项,即设置key的过期时间为3秒。
"3".getBytes(StandardCharsets.UTF_8)表示设置key的过期时间为3秒。
connection.execute方法返回的是一个Object类型的结果,需要将其转换为String类型并返回。 总体来说,这段代码的作用是在Redis中执行一个set命令,将key和value存储到Redis中,并设置了过期时间和NX选项,确保只有在key不存在时才进行set操作。

    当多个节点同时尝试执行set操作来设置同一个key时,只有一个节点能够成功设置,因为Redis中的set命令默认具有原子性。如果设置了NX选项,即只有在key不存在时才进行set操作,那么只有第一个节点能够成功设置该key,其他节点将无法设置。 通过利用这个特性,可以将某个共享资源对应的key作为锁的名称,多个节点试图通过set操作来竞争该锁。只有一个节点能够成功设置该锁的key,即获得了分布式锁。其他节点则在设置失败后,可以选择等待或者进行其他处理。 同时,为了避免因为某个节点获得锁后发生故障而导致锁一直无法释放,还可以为锁设置过期时间。当锁的持有者在一定时间后未能释放锁,锁将自动过期并被其他节点获取。 综上所述,通过使用Redis的set操作和一些选项参数,可以实现简单的分布式锁。多个节点可以通过竞争设置同一个key来获得锁,并通过设置过期时间来避免因为锁的持有者发生故障而导致锁一直无法释放。

————————————————

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
11月前
|
监控 Java API
Spring Boot 3.2 结合 Spring Cloud 微服务架构实操指南 现代分布式应用系统构建实战教程
Spring Boot 3.2 + Spring Cloud 2023.0 微服务架构实践摘要 本文基于Spring Boot 3.2.5和Spring Cloud 2023.0.1最新稳定版本,演示现代微服务架构的构建过程。主要内容包括: 技术栈选择:采用Spring Cloud Netflix Eureka 4.1.0作为服务注册中心,Resilience4j 2.1.0替代Hystrix实现熔断机制,配合OpenFeign和Gateway等组件。 核心实操步骤: 搭建Eureka注册中心服务 构建商品
1478 3
|
负载均衡 算法 关系型数据库
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
本文聚焦 MySQL 集群架构中的负载均衡算法,阐述其重要性。详细介绍轮询、加权轮询、最少连接、加权最少连接、随机、源地址哈希等常用算法,分析各自优缺点及适用场景。并提供 Java 语言代码实现示例,助力直观理解。文章结构清晰,语言通俗易懂,对理解和应用负载均衡算法具有实用价值和参考价值。
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
|
9月前
|
监控 Cloud Native Java
Spring Boot 3.x 微服务架构实战指南
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Spring Boot 3.x与微服务架构,探索云原生、性能优化与高可用系统设计。以代码为笔,在二进制星河中谱写极客诗篇。关注我,共赴技术星辰大海!(238字)
1373 2
Spring Boot 3.x 微服务架构实战指南
|
10月前
|
消息中间件 Ubuntu Java
SpringBoot整合MQTT实战:基于EMQX实现双向设备通信
本教程指导在Ubuntu上部署EMQX 5.9.0并集成Spring Boot实现MQTT双向通信,涵盖服务器搭建、客户端配置及生产实践,助您快速构建企业级物联网消息系统。
3009 1
|
开发框架 Java 关系型数据库
在Linux系统中安装JDK、Tomcat、MySQL以及部署J2EE后端接口
校验时,浏览器输入:http://[your_server_IP]:8080/myapp。如果你看到你的应用的欢迎页面,恭喜你,一切都已就绪。
837 17
|
监控 Java 调度
SpringBoot中@Scheduled和Quartz的区别是什么?分布式定时任务框架选型实战
本文对比分析了SpringBoot中的`@Scheduled`与Quartz定时任务框架。`@Scheduled`轻量易用,适合单机简单场景,但存在多实例重复执行、无持久化等缺陷;Quartz功能强大,支持分布式调度、任务持久化、动态调整和失败重试,适用于复杂企业级需求。文章通过特性对比、代码示例及常见问题解答,帮助开发者理解两者差异,合理选择方案。记住口诀:单机简单用注解,多节点上Quartz;若是任务要可靠,持久化配置不能少。
1123 4
|
关系型数据库 MySQL Linux
CentOS 7系统下详细安装MySQL 5.7的步骤:包括密码配置、字符集配置、远程连接配置
以上就是在CentOS 7系统下安装MySQL 5.7的详细步骤。希望这个指南能帮助你顺利完成安装。
3354 26
|
监控 Java 关系型数据库
Spring Boot整合MySQL主从集群同步延迟解决方案
本文针对电商系统在Spring Boot+MyBatis架构下的典型问题(如大促时订单状态延迟、库存超卖误判及用户信息更新延迟)提出解决方案。核心内容包括动态数据源路由(强制读主库)、大事务拆分优化以及延迟感知补偿机制,配合MySQL参数调优和监控集成,有效将主从延迟控制在1秒内。实际测试表明,在10万QPS场景下,订单查询延迟显著降低,超卖误判率下降98%。
577 5
|
10月前
|
缓存 关系型数据库 BI
使用MYSQL Report分析数据库性能(下)
使用MYSQL Report分析数据库性能
588 158

推荐镜像

更多