咖啡汪日志——高可用分布式点赞缓存系统(redis)

一边听着舒缓的轻音乐,一边写博客
最近是真的有点累了呢
原来精力再旺盛的哈士奇
也会有疲乏的时候呢
在这里插入图片描述

开篇有益:

为什么点赞信息要放在缓存中?
考虑一下:
你的新发布app,号称两亿人同时在线
中午吃饭时间,大家都休息
点赞排行榜,所有人都想看,高频操作,
同一时间数万查询量,怎么处理?

那么既然要放缓存里,又该如何去放呢?
这可是个技术活噢
大家先来看一下咖啡汪的思维导图,源码链接依旧是附在了思维导图后面

思维导图:(一张图截不下。。。)

在这里插入图片描述
在这里插入图片描述

源码链接:
https://github.com/HuskyCorps/distributeMiddleware

让我们来看一下程序(为了方便大家查看,本汪在代码上做了详细的注释):

1.我们配置redis模板
(不启用她的事务支持也不使用管道,至于为什么不启用,大家可以查看本汪在Boolean enablePraise = redisTemplate.opsForValue().setIfAbsent(recordKey,1);这行代码上的注释)
StringRedisTemplate 是RedisTemplate 的一个子类,这儿不做过多阐述。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * redis自定义注入-redis模板
 *
 * @author Yuezejian
 * @date 2020年 08月25日 20:47:30
 */
@Configuration
public class RedisConfig {
    @Autowired
    private RedisConnectionFactory connectionFactory;

    @Bean
    public RedisTemplate redisTemplate() {
        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);
        //设置key/value的序列化策略
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        //也可以使用new JdkSerializationRedisSerializer(),反序列化时注意匹配
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;

    }

    // class StringRedisTemplate extends RedisTemplate<String, String>
    //是子类
    @Bean
    public StringRedisTemplate stringRedisTemplate() {
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate(connectionFactory);
        //开启事务支持
        stringRedisTemplate.setEnableTransactionSupport(true);
        return stringRedisTemplate;
    }
}

2,Controller中我们写了
获取文章列表, 获取文章详情及点赞信息, 文章点赞, 取消文章点赞,
获取当前用户点赞过的历史文章-用户详情, 获取文章点赞排行榜 ,这几块儿功能接口
太简单的查询,本汪不做太多描述,点赞的sql也都简单,本汪不细说
本汪先带大家,一起看一下他们的实现思路:


import com.tencent.bigdata.convenience.api.response.BaseResponse;
import com.tencent.bigdata.convenience.api.response.StatusCode;
import com.tencent.bigdata.convenience.model.dto.PraiseDto;
import com.tencent.bigdata.convenience.model.entity.Article;
import com.tencent.bigdata.convenience.model.mapper.ArticleMapper;
import com.tencent.bigdata.convenience.server.service.article.PraiseService;
import com.tencent.bigdata.convenience.server.service.log.LogAopAnnotation;
import com.tencent.bigdata.convenience.server.utils.ValidatorUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;


/**
 * 文章点赞Controller
 *
 * @author Yuezejian
 * @date 2020年 09月10日 19:59:46
 */
@RestController
@RequestMapping("praise")
public class PraiseController extends AbstractController{

    @Autowired
    private PraiseService praiseService;

    @Autowired
    private ArticleMapper articleMapper;

    /**
     * 获取文章列表
     * @return
     */
    @RequestMapping(value = "getArticleList", method = RequestMethod.GET)
    public BaseResponse getArticleList() {
        BaseResponse response = new BaseResponse(StatusCode.Success);
        try {
            response.setData(praiseService.getAll());
        } catch (Exception e) {
            response = new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
        }
        return response;
    }

    /**
     * 获取文章详情及点赞信息
     * @param articleId
     * @param curUserId
     * @return
     */
    @RequestMapping("getArticleInfo")
    public BaseResponse getArticleInfo(@RequestParam Integer articleId,Integer curUserId) {
        BaseResponse response = new BaseResponse(StatusCode.Success);
        try {
            response.setData(praiseService.getArticleInfo(articleId,curUserId));

        } catch (Exception e) {
            response = new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
        }
        return response;
    }

    /**
     * 文章点赞
     * @param dto
     * @param result
     * @return
     */
    @RequestMapping(value = "on",method = RequestMethod.POST,consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
    @LogAopAnnotation(value = "文章点赞",operatorTable = "article_praise和article")
    public BaseResponse praiseOn(@RequestBody @Validated PraiseDto dto, BindingResult result) {
        String resCheck = ValidatorUtil.checkResult(result);
        if (StringUtils.isNotBlank(resCheck)) {
            return new BaseResponse(StatusCode.InvalidParams.getCode(),resCheck);
        }
        BaseResponse response = new BaseResponse(StatusCode.Success);
        try {
            //TODO:查询点赞文章是否失效
            Article article = articleMapper.selectByPrimaryKey(dto.getArticleId());
            if ( article != null) {
                if (praiseService.praiseOn(dto)) {
                    response.setData("点赞成功");
                } else {
                    response.setData("该文章已经点赞过了");
                }
            } else {
                response.setData("该文章已下架或不存在");
            }
        } catch (Exception e) {
            response = new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
        }
        return response;
    }

    /**
     * 取消文章点赞
     * @param dto
     * @param result
     * @return
     */
    @RequestMapping(value = "off",method = RequestMethod.POST,consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
    @LogAopAnnotation(value = "取消文章点赞",operatorTable = "article_praise和article")
    public BaseResponse praiseOff(@RequestBody @Validated PraiseDto dto, BindingResult result) {
        String resCheck = ValidatorUtil.checkResult(result);
        if (StringUtils.isNotBlank(resCheck)) {
            return new BaseResponse(StatusCode.InvalidParams.getCode(),resCheck);
        }
        BaseResponse response = new BaseResponse(StatusCode.Success);
        try {
            //TODO:查询点赞文章是否失效
            Article article = articleMapper.selectByPrimaryKey(dto.getArticleId());
            if (article != null) {

                if (praiseService.praiseOff(dto)) {
                    response.setData("已成功取消点赞");
                } else {
                    response.setData("您还未点赞过该文章");
                }
            } else {
                response.setData("该文章已下架或不存在");
            }

        } catch (Exception e) {
            response = new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
        }
        return response;
    }

    /**
     * 获取当前用户点赞过的历史文章-用户详情
     * @param currentUserId
     * @return
     */
    @RequestMapping(value = "praiseHistory",method = RequestMethod.POST)
    public BaseResponse getHistoryPraiseArticleList(@RequestParam Integer currentUserId) {
        if (currentUserId == null || currentUserId < 0){
            return new BaseResponse(StatusCode.InvalidParams);
        }
        BaseResponse response = new BaseResponse(StatusCode.Success);
        try {

            response.setData(praiseService.getPraiseHistoryArticle(currentUserId));

        } catch (Exception e) {
            response = new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
        }
        return response;
    }

    /**
     * 获取文章点赞排行榜
     * @return
     */
    @RequestMapping(value = "ranks" ,method = RequestMethod.POST)
    public BaseResponse getRankingList() {
        BaseResponse response = new BaseResponse(StatusCode.Success);
        try {
            response.setData(praiseService.getPraiseRanks());

        } catch (Exception e) {
            response = new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
        }
        return response;
    }




}

service:


import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.tencent.bigdata.convenience.model.dto.ArticlePraiseRankDto;
import com.tencent.bigdata.convenience.model.dto.PraiseDto;
import com.tencent.bigdata.convenience.model.entity.Article;
import com.tencent.bigdata.convenience.model.entity.ArticlePraise;
import com.tencent.bigdata.convenience.model.entity.User;
import com.tencent.bigdata.convenience.model.mapper.ArticleMapper;
import com.tencent.bigdata.convenience.model.mapper.ArticlePraiseMapper;
import com.tencent.bigdata.convenience.model.mapper.UserMapper;
import com.tencent.bigdata.convenience.server.enums.Constant;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 文章点赞Service
 *
 * @author Yuezejian
 * @date 2020年 09月10日 21:54:36
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class PraiseService {

    @Autowired
    private ArticleMapper articleMapper;

    @Autowired
    private ArticlePraiseMapper praiseMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserMapper userMapper;

    /**
     * 文章点赞
     * @param dto
     * @return
     * @throws Exception
     */
    public Boolean praiseOn(PraiseDto dto) throws Exception{
        //将基于redis的缓存判断(redis指令:serNX,成功 返true)
        final String recordKey = Constant.RedisArticlePraiseUser + dto.getUserId() + dto.getArticleId();
        //TODO:setIfAbsent()可以解决并发安全问题(基于原子性,底层线性调用)
        //TODO:注意(当开启事务支持或管道通信时,调用setIfAbsent()会返回null)
        //TODO:把recordKey当Key ,1 当value, 往redis里存,如果存入成功,返回true; 如果存入时发现已经存过了,返回false;
        Boolean enablePraise = redisTemplate.opsForValue().setIfAbsent(recordKey,1);
        if (enablePraise) {
            //TODO:缓存被穿透时,需要查数据库的记录
            //TODO:因此我们不能认为缓存是绝对可靠的,进入此处的数据,应当查询数据库是否已经有了点赞记录
            //TODO:有则不允许点赞,没有才允许点赞
            //TODO:将点赞的数据插入DB
            ArticlePraise entry = new ArticlePraise(dto.getArticleId(), dto.getUserId(), DateTime.now().toDate());
            int res = praiseMapper.insertSelective(entry);
            if (res > 0) {
                articleMapper.updatePraiseTotal(dto.getArticleId(), 1);
                //TODO:缓存点赞的相关信息
                this.cachePraiseOn(dto);
                //TODO:
            }
        }

        return enablePraise;
    }

    /**
     * 取消文章点赞
     * @param dto
     * @return
     * @throws Exception
     */
    public Boolean praiseOff(PraiseDto dto) throws Exception {
        final String recordKey = Constant.RedisArticlePraiseUser + dto.getUserId() + dto.getArticleId();
        //判断是否有点赞记录,有才能取消
        Boolean hasPraise = redisTemplate.hasKey(recordKey);
        if (hasPraise) {
            //删除DB中的记录
            int res = praiseMapper.cancelPraise(dto.getArticleId(), dto.getUserId());
            if (res > 0) {
                //移除缓存中,用户的点赞记录
                redisTemplate.delete(recordKey);

                //更新文章的点赞总数
                articleMapper.updatePraiseTotal(dto.getArticleId(), -1);

                //TODO:缓存取消点赞的相关信息
                cachePraiseOff(dto);

            }
        }
        return hasPraise;
    }

    /**
     * 获取文章列表
     * @return
     * @throws Exception
     */
    public List<Article> getAll() {
        return articleMapper.selectAll();
    }

    /**
     * 获取文章详情列表
     * @param articleId 文章ID
     * @param curUserId 当前用户ID
     * @return
     */
    public Map<String, Object> getArticleInfo(final Integer articleId, final Integer curUserId) {
        Map<String, Object> resMap = Maps.newHashMap();
        //TODO:获取文章详情信息
        resMap.put("article-文章详情",articleMapper.selectByPK(articleId));

        //TODO:获取点赞过当前文章的用户列表->获取用户昵称,用","拼接
        HashOperations<String,String,Set<Integer>> praiseHash = redisTemplate.opsForHash();
        Set<Integer> uIds = praiseHash.get(Constant.RedisArticlePraiseHashKey,articleId.toString());
        if (uIds != null && !uIds.isEmpty()) {
            //把id,用逗号拼接起来,sql用in(1,3,4)去查
            resMap.put("userIds-用户Id列表",uIds);
            String ids= Joiner.on(",").join(uIds);
            //把查询结果用逗号拼接,返回前端,(李白,安琪拉,孙尚香)
            //java8的Stream流,不是什么新东西,jdk1.8是2014年3月18日发版的,六年前的东西了
            //不懂的自行查看本汪得相关博客
            String names = userMapper.selectNamesById(ids)
                    .stream().map(User::getName).collect(Collectors.joining(","));
            resMap.put("uersNames-用户姓名列表",names);

            //TODO:当前用户是否点赞过这篇文章
            if (curUserId != null) {
                resMap.put("isCurUserPraiseCurArtilce",uIds.contains(curUserId));
            }
        } else {
            resMap.put("userIds-用户Id列表",null);
            resMap.put("uersNames-用户姓名列表",null);
            resMap.put("isCurUserPraiseCurArtilce",false);
        }
        return resMap;
    }

    /**
     * 获取文章点赞排行榜
     * @return
     */
    public Map<String,Object> getPraiseRanks() {
        Map<String,Object> resMap = Maps.newHashMap();
        //TODO:获取点赞排行榜
        List<ArticlePraiseRankDto> rankList = Lists.newLinkedList();
        ZSetOperations<String,String> praiseSort = redisTemplate.opsForZSet();
        Long total = praiseSort.size(Constant.RedisArticlePraiseSortKey);
        //倒序排列,取从(0-total)的数据
        Set<String> set = praiseSort.reverseRange(Constant.RedisArticlePraiseSortKey,0L,total);
        if (set != null && !set.isEmpty()) {
            set.forEach(value -> {
                //TODO:拿出他的得分情况
                Double score =  praiseSort.score(Constant.RedisArticlePraiseSortKey,value);
                if (score > 0) {
                    //TODO: 拆分文章id,文章标题
                    Integer pos = StringUtils.indexOf(value, Constant.SplitChar);
                    if (pos > 0) {
                        String articleId = StringUtils.substring(value, 0, pos);
                        String articleTitle = StringUtils.substring(value, pos + 1);
                        rankList.add(new ArticlePraiseRankDto(articleId, articleTitle, score.toString(), score));
                    }
                }
            });
        }
        resMap.put("articlePraiseRank-文章点赞排行榜",rankList);
        return resMap;

    }

    /**
     * 获取用户点赞过的历史文章
     * @param currentUserId
     * @return
     */
    public Map<String,Object> getPraiseHistoryArticle(final Integer currentUserId) {
        Map<String,Object> resMap = Maps.newHashMap();

        //TODO:查询用户详情
        resMap.put("用户详情",userMapper.selectByPrimaryKey(currentUserId));

        //TODO:用户点赞过的历史文章
        List<PraiseDto> userPraiseArtilces = Lists.newLinkedList();
       HashOperations<String,String,String> hash = redisTemplate.opsForHash();
       //取出"用户点赞标识符",所对应存储的field-value键值对
        //本汪提一嘴,别忘了,有点赞的文章,value为【文章标题】;已取消点赞的文章,value是被置空的
        //是在【cacheUserPraiseArticle】设置的哦
       Map<String,String> map =  hash.entries(Constant.RedisArticleUserPraiseKey);
       map.entrySet().forEach(entity -> {
           String field = entity.getKey();
           String value = entity.getValue();
           String[] arr = StringUtils.split(field,Constant.SplitChar);
           //判断 value是否为空-如果为空,则代表用户已经取消点赞,【cacheUserPraiseArticle】
           if (StringUtils.isNotBlank(value)) {
               //现在通过遍历+判断,来筛选出当前用户点赞过的文章
               if (String.valueOf(currentUserId).equals(arr[0])) {
                   userPraiseArtilces.add(new PraiseDto(currentUserId,Integer.valueOf(arr[1]),value));
               }
           }
       });
       //TODO:
        resMap.put("用户点赞过的文章列表",userPraiseArtilces);
        return resMap;
    }

    //缓存点赞相关的信息
    //这边都做了什么呢,本汪说下
    //HashOperators,实际是Hash<k,Map<K,V>>  ——>  Hash<String,Map<String,Set<Integer>>>
    //设置缓存时,首先通过Constant.RedisArticlePraiseHashKey这个固定的字符串 + 文章id,来对缓存进行功能模块的分组
    // 获取时通过praise+文章id,锁定是点赞缓存里,某个固定的文章,查看点赞该文章的用户id组
    //而Set<Integer>里,存放着的是所有点赞过该文章的用户的id组【1001,1002,2003】
    //说明用户1001等人已经点赞过该文章

    /**
     * 缓存点赞
     * @param dto
     * @throws Exception
     */
    private void cachePraiseOn(final PraiseDto dto) {
        //选择的数据结构为Hash, Key --字符串, 存储redis的标志;field -文章id ; Value - 用户id列表
        HashOperations<String,String, Set<Integer>> praiseHash = redisTemplate.opsForHash();

        //记录点赞过当前文章的用户id列表
        Set<Integer> uIds = praiseHash.get(Constant.RedisArticlePraiseHashKey,dto.getArticleId().toString());
        if (uIds == null || uIds.isEmpty()) {
            uIds = Sets.newHashSet();
        }
        uIds.add(dto.getUserId());
        praiseHash.put(Constant.RedisArticlePraiseHashKey,dto.getArticleId().toString(),uIds);

        //TODO:缓存点赞排行榜
        this.cachePraiseRank(dto,uIds.size());

        //TODO:缓存用户的点赞轨迹(用户点赞过的历史文章)
        this.cacheUserPraiseArticle(dto,true);

    }

    /**
     * 缓存取消点赞时的相关信息
     * @param dto
     * @throws Exception
     */
    private void cachePraiseOff(final PraiseDto dto) {
        //选择的数据结构为Hash, Key --字符串, 存储redis的标志;field -文章id ; Value - 用户id列表
        HashOperations<String,String, Set<Integer>> praiseHash = redisTemplate.opsForHash();

        //查询点赞过当前文章的用户id列表
        Set<Integer> uIds = praiseHash.get(Constant.RedisArticlePraiseHashKey,dto.getArticleId().toString());
        if (uIds != null && !uIds.isEmpty() && uIds.contains(dto.getUserId())) {
            //如果有当前用户的点赞记录,就移除
           uIds.remove(dto.getUserId());
           //把移除后的数据重新放入Set集合
            praiseHash.put(Constant.RedisArticlePraiseHashKey,dto.getArticleId().toString(),uIds);
        }
        //TODO:缓存点赞排行榜
        this.cachePraiseRank(dto,uIds.size());

        //TODO:缓存用户的点赞轨迹(用户点赞过的历史文章,用户维护)
        this.cacheUserPraiseArticle(dto,false);
    }

    /**
     * 缓存点赞排行榜(SortedSet->ZSet)
     * value为 "文章ID" + "_" + "文章标题"
     * score为 点赞总数
     */
    private void cachePraiseRank(final PraiseDto dto,final Integer total) {
        String value =dto.getArticleId()+Constant.SplitChar+dto.getTitle();
        ZSetOperations<String,String> praiseSort = redisTemplate.opsForZSet();
        //TODO:删除原始数据
        praiseSort.remove(Constant.RedisArticlePraiseSortKey,value);
        //@Nullable
        //	Boolean add(K key, V value, double score);
        //TODO:存入最新现在排行榜数据
        praiseSort.add(Constant.RedisArticlePraiseSortKey,value,total.doubleValue());
    }

    /**
     * 以用户为维度,存储用户点赞过的信息
     * key = 存储到redis的标准符
     * field = 用户id + "-" 文章id
     * value = 文章标题
     * @param dto
     * @param isOn true:点赞直接存储  false:取消点赞,重置value为null/""
     */
    private void cacheUserPraiseArticle(final PraiseDto dto, Boolean isOn) {
        final String field = dto.getUserId() + Constant.SplitChar + dto.getArticleId();
        HashOperations<String,String,String> hash = redisTemplate.opsForHash();
        if (isOn) {
            hash.put(Constant.RedisArticleUserPraiseKey,field, dto.getTitle());
        } else {
            hash.put(Constant.RedisArticleUserPraiseKey,field,"");
        }

    }


}

运行结果:
在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
概要介绍:本门课程属于“Java分布式中间件大汇聚实战”系列课程,主要介绍了企业级项目中真实的应用场景的实现及主流的Java核心技术栈(Redis、RabbitMQ、Spring AOP、Redisson、ZooKeeper…)的实战等等。除此之外,还介绍了如何基于Redis设计并实战一款点赞系统点赞、取消点赞、排行榜、用户中心、文章点赞用户列表…)可以说技术干货甚多,不仅可以巩固企业级应用系统的开发实战能力,相信在面试、跳槽涨薪方面也能带来相应的帮助!课程内容:传说中的金三银四、面试跳槽涨薪季已经来临,Debug特地为大家准备了一系列跟面试、跳槽、巩固核心技术栈相关的课程,本门课程属于第一季,其中的内容包括企业级项目中真实的应用场景实战、面试相关的技术点分享、主流的Java技术栈(Undertow、Redis、RabbitMQ、Spring AOP、Redisson、ZooKeeper…)实战等等。除此之外,我们还基于Redis设计并实战了一款点赞系统,可以说技术干货甚多。在课程的最后,Debug给大家整理了一份最新的面向BAT大厂招聘 ~ 2020年程序猿最新的Java面试题(附带目录和答案),希望对各位小伙伴的成长有所帮助!值得一提的是,本季课程实战的应用场景包括“日志记录”、“邮件发送”、“通告消息通知”、“短信验证码失效验证”、“会员到期自动提醒/到期前N天自动提醒”以及“点赞系统”的设计与实战,其大纲如下所示:其中,涉及到的技术栈包括Spring Boot2.0、Mybatis、Undertow、Redis、RabbitMQ、Redisson、Spring AOP、 Java8…下面罗列出本门课程重点介绍的价格应用案例以及业务场景的实现流程图!(1)基于Spring的消息驱动模型实现日志的异步记录:(2)基于消息中间件RabbitMQ的消息队列实现日志的异步记录:(3)基于缓存中间件Redis的订阅发布机制实现商户公告消息通知:(4)基于Redis的Key失效与定时任务实现实现短信验证码的过期失效验证:其他核心、典型的应用案例和业务场景的实战可以详细参考“课程目录”!除此之外,我们还基于缓存中间件Redis设计并实战实现了点赞系统中的点赞功能模块,下面罗列出其中涉及到的相关功能模块的实战流程图:其课程收益如下所示:
引用提到了Redis,它是一种常用的分布式缓存系统Redis是一个开源的、基于内存的数据存储系统,它可以用作缓存、消息队列、持久化等多种用途。由于Redis具有高性能、高可用性和可扩展性,所以在分布式环境中广泛应用于缓存数据的存储和访问。Redis使用键值对的方式存储数据,并支持各种数据类型的操作,如字符串、哈希、列表、集合和有序集合等。在分布式缓存中,Redis通过在内存中存储数据,提供了快速的读写操作,以加速应用程序的性能。同时,Redis还具有分布式锁的功能,可以保证在分布式环境中的并发访问的一致性。引用提到了Redission,它是一个基于Redis实现的分布式锁框架。Redission提供了多种分布式锁的实现方式,包括基于Redis分布式锁。通过使用Redission,可以很方便地在分布式环境中实现锁机制,保证数据的一致性和并发访问的正确性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Redis分布式缓存详解)](https://blog.csdn.net/chenjiap/article/details/131630981)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咖啡汪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值