成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Redis詳解 - SpringBoot整合Redis,RedisTemplate和注解兩種方式的使

SexySix / 497人閱讀

摘要:和注解的方法返回值要一致刪除緩存在需要刪除緩存的方法上加注解,執(zhí)行完這個方法之后會將中對應(yīng)的記錄刪除。代表返回值,意思是當(dāng)返回碼不等于時不緩存,也就是等于時才緩存。返回值特定值如果被設(shè)置了如果沒有被設(shè)置例子自動將對應(yīng)到并且返回原來對應(yīng)的。

本文主要講 Redis 的使用,如何與 SpringBoot 項(xiàng)目整合,如何使用注解方式和 RedisTemplate 方式實(shí)現(xiàn)緩存。最后會給一個用 Redis 實(shí)現(xiàn)分布式鎖,用在秒殺系統(tǒng)中的案例。

更多 Redis 的實(shí)際運(yùn)用場景請關(guān)注開源項(xiàng)目 coderiver

項(xiàng)目地址:https://github.com/cachecats/...

一、NoSQL 概述 什么是 NoSQL ?

NoSQL(NoSQL = Not Only SQL ),意即“不僅僅是SQL”,泛指非關(guān)系型的數(shù)據(jù)庫。

為什么需要 NoSQL ?

隨著互聯(lián)網(wǎng)web2.0網(wǎng)站的興起,傳統(tǒng)的關(guān)系數(shù)據(jù)庫在應(yīng)付web2.0網(wǎng)站,特別是超大規(guī)模和高并發(fā)的SNS類型的web2.0純動態(tài)網(wǎng)站已經(jīng)顯得力不從心,暴露了很多難以克服的問題,而非關(guān)系型的數(shù)據(jù)庫則由于其本身的特點(diǎn)得到了非常迅速的發(fā)展。NoSQL數(shù)據(jù)庫的產(chǎn)生就是為了解決大規(guī)模數(shù)據(jù)集合多重?cái)?shù)據(jù)種類帶來的挑戰(zhàn),尤其是大數(shù)據(jù)應(yīng)用難題。 -- 百度百科

NoSQL 數(shù)據(jù)庫的四大分類

鍵值(key-value)存儲

列存儲

文檔數(shù)據(jù)庫

圖形數(shù)據(jù)庫

分類 相關(guān)產(chǎn)品 典型應(yīng)用 數(shù)據(jù)模型 優(yōu)點(diǎn) 缺點(diǎn)
鍵值(key-value) Tokyo、 Cabinet/Tyrant、Redis、Voldemort、Berkeley DB 內(nèi)容緩存,主要用于處理大量數(shù)據(jù)的高訪問負(fù)載 一系列鍵值對 快速查詢 存儲的數(shù)據(jù)缺少結(jié)構(gòu)化
列存儲數(shù)據(jù)庫 Cassandra, HBase, Riak 分布式的文件系統(tǒng) 以列簇式存儲,將同一列數(shù)據(jù)存在一起 查找速度快,可擴(kuò)展性強(qiáng),更容易進(jìn)行分布式擴(kuò)展 功能相對局限
文檔數(shù)據(jù)庫 CouchDB, MongoDB Web應(yīng)用(與Key-Value類似,value是結(jié)構(gòu)化的) 一系列鍵值對 數(shù)據(jù)結(jié)構(gòu)要求不嚴(yán)格 查詢性能不高,而且缺乏統(tǒng)一的查詢語法
圖形(Graph)數(shù)據(jù)庫 Neo4J, InfoGrid, Infinite Graph 社交網(wǎng)絡(luò),推薦系統(tǒng)等。專注于構(gòu)建關(guān)系圖譜 圖結(jié)構(gòu) 利用圖結(jié)構(gòu)相關(guān)算法 需要對整個圖做計(jì)算才能得出結(jié)果,不容易做分布式集群方案
NoSQL 的特點(diǎn)

易擴(kuò)展

靈活的數(shù)據(jù)模型

大數(shù)據(jù)量,高性能

高可用

二、Redis 概述 Redis的應(yīng)用場景

緩存

任務(wù)隊(duì)列

網(wǎng)站訪問統(tǒng)計(jì)

應(yīng)用排行榜

數(shù)據(jù)過期處理

分布式集群架構(gòu)中的 session 分離

Redis 安裝

網(wǎng)上有很多 Redis 的安裝教程,這里就不多說了,只說下 Docker 的安裝方法:

Docker 安裝運(yùn)行 Redis

docker run -d -p 6379:6379 redis:4.0.8

如果以后想啟動 Redis 服務(wù),打開命令行,輸入以下命令即可。

redis-server

使用前先引入依賴


    org.springframework.boot
    spring-boot-starter-data-redis
三、注解方式使用 Redis 緩存

使用緩存有兩個前置步驟

pom.xml 引入依賴


    org.springframework.boot
    spring-boot-starter-data-redis

在啟動類上加注解 @EnableCaching

@SpringBootApplication
@EnableCaching
public class SellApplication {
    public static void main(String[] args) {
        SpringApplication.run(SellApplication.class, args);
    }
}

常用的注解有以下幾個

@Cacheable

屬性如下圖

用于查詢和添加緩存,第一次查詢的時候返回該方法返回值,并向 Redis 服務(wù)器保存數(shù)據(jù)。

以后調(diào)用該方法先從 Redis 中查是否有數(shù)據(jù),如果有直接返回 Redis 緩存的數(shù)據(jù),而不執(zhí)行方法里的代碼。如果沒有則正常執(zhí)行方法體中的代碼。

value 或 cacheNames 屬性做鍵,key 屬性則可以看作為 value 的子鍵, 一個 value 可以有多個 key 組成不同值存在 Redis 服務(wù)器。

驗(yàn)證了下,value 和 cacheNames 的作用是一樣的,都是標(biāo)識主鍵。兩個屬性不能同時定義,只能定義一個,否則會報(bào)錯。

condition 和 unless 是條件,后面會講用法。其他的幾個屬性不常用,其實(shí)我也不知道怎么用…

@CachePut

更新 Redis 中對應(yīng)鍵的值。屬性和 @Cacheable 相同

@CacheEvict

刪除 Redis 中對應(yīng)鍵的值。

3.1 添加緩存

在需要加緩存的方法上添加注解 @Cacheable(cacheNames = "product", key = "123"),

cacheNameskey 都必須填,如果不填 key ,默認(rèn)的 key 是當(dāng)前的方法名,更新緩存時會因?yàn)榉椒煌率 ?/p>

如在訂單列表上加緩存

    @RequestMapping(value = "/list", method = RequestMethod.GET)
    @Cacheable(cacheNames = "product", key = "123")
    public ResultVO list() {

        // 1.查詢所有上架商品
        List productInfoList = productInfoService.findUpAll();

        // 2.查詢類目(一次性查詢)
        //用 java8 的特性獲取到上架商品的所有類型
        List categoryTypes = productInfoList.stream().map(e -> e.getCategoryType()).collect(Collectors.toList());
        List productCategoryList = categoryService.findByCategoryTypeIn(categoryTypes);

        List productVOList = new ArrayList<>();
        //數(shù)據(jù)拼裝
        for (ProductCategory category : productCategoryList) {
            ProductVO productVO = new ProductVO();
            //屬性拷貝
            BeanUtils.copyProperties(category, productVO);
            //把類型匹配的商品添加進(jìn)去
            List productInfoVOList = new ArrayList<>();
            for (ProductInfo productInfo : productInfoList) {
                if (productInfo.getCategoryType().equals(category.getCategoryType())) {
                    ProductInfoVO productInfoVO = new ProductInfoVO();
                    BeanUtils.copyProperties(productInfo, productInfoVO);
                    productInfoVOList.add(productInfoVO);
                }
            }
            productVO.setProductInfoVOList(productInfoVOList);
            productVOList.add(productVO);
        }

        return ResultVOUtils.success(productVOList);
    }

可能會報(bào)如下錯誤

對象未序列化。讓對象實(shí)現(xiàn) Serializable 方法即可

@Data
public class ProductVO implements Serializable {
    
    private static final long serialVersionUID = 961235512220891746L;

    @JsonProperty("name")
    private String categoryName;

    @JsonProperty("type")
    private Integer categoryType;

    @JsonProperty("foods")
    private List productInfoVOList ;
}

生成唯一的 id 在 IDEA 里有一個插件:GenerateSerialVersionUID 比較方便。

重啟項(xiàng)目訪問訂單列表,在 rdm 里查看 Redis 緩存,有 product::123 說明緩存成功。

3.2 更新緩存

在需要更新緩存的方法上加注解: @CachePut(cacheNames = "prodcut", key = "123")

注意

cacheNameskey 要跟 @Cacheable() 里的一致,才會正確更新。

@CachePut()@Cacheable() 注解的方法返回值要一致

3.3 刪除緩存

在需要刪除緩存的方法上加注解:@CacheEvict(cacheNames = "prodcut", key = "123"),執(zhí)行完這個方法之后會將 Redis 中對應(yīng)的記錄刪除。

3.4 其他常用功能

cacheNames 也可以統(tǒng)一寫在類上面, @CacheConfig(cacheNames = "product") ,具體的方法上就不用寫啦。

@CacheConfig(cacheNames = "product")
public class BuyerOrderController {
    @PostMapping("/cancel")
    @CachePut(key = "456")
    public ResultVO cancel(@RequestParam("openid") String openid,
                           @RequestParam("orderId") String orderId){
        buyerService.cancelOrder(openid, orderId);
        return ResultVOUtils.success();
    }
}

Key 也可以動態(tài)設(shè)置為方法的參數(shù)

@GetMapping("/detail")
@Cacheable(cacheNames = "prodcut", key = "#openid")
public ResultVO detail(@RequestParam("openid") String openid,
                             @RequestParam("orderId") String orderId){
    OrderDTO orderDTO = buyerService.findOrderOne(openid, orderId);
    return ResultVOUtils.success(orderDTO);
}

如果參數(shù)是個對象,也可以設(shè)置對象的某個屬性為 key。比如其中一個參數(shù)是 user 對象,key 可以寫成 key="#user.id"

緩存還可以設(shè)置條件。

設(shè)置當(dāng) openid 的長度大于3時才緩存

@GetMapping("/detail")
@Cacheable(cacheNames = "prodcut", key = "#openid", condition = "#openid.length > 3")
public ResultVO detail(@RequestParam("openid") String openid,
                                 @RequestParam("orderId") String orderId){
    OrderDTO orderDTO = buyerService.findOrderOne(openid, orderId);
    return ResultVOUtils.success(orderDTO);
}

還可以指定 unless 即條件不成立時緩存。#result 代表返回值,意思是當(dāng)返回碼不等于 0 時不緩存,也就是等于 0 時才緩存。

@GetMapping("/detail")
@Cacheable(cacheNames = "prodcut", key = "#openid", condition = "#openid.length > 3", unless = "#result.code != 0")
public ResultVO detail(@RequestParam("openid") String openid,
                                 @RequestParam("orderId") String orderId){
    OrderDTO orderDTO = buyerService.findOrderOne(openid, orderId);
    return ResultVOUtils.success(orderDTO);
}

四、RedisTemplate 使用 Redis 緩存

與使用注解方式不同,注解方式可以零配置,只需引入依賴并在啟動類上加上 @EnableCaching 注解就可以使用;而使用 RedisTemplate 方式麻煩些,需要做一些配置。

4.1 Redis 配置

第一步還是引入依賴和在啟動類上加上 @EnableCaching 注解。

然后在 application.yml 文件中配置 Redis

spring:
  redis:
    port: 6379
    database: 0
    host: 127.0.0.1
    password:
    jedis:
      pool:
        max-active: 8
        max-wait: -1ms
        max-idle: 8
        min-idle: 0
    timeout: 5000ms

然后寫個 RedisConfig.java 配置類

package com.solo.coderiver.user.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
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.Jackson2JsonRedisSerializer;

import java.net.UnknownHostException;


@Configuration
public class RedisConfig {

    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate redisTemplate(
            RedisConnectionFactory redisConnectionFactory)
            throws UnknownHostException {

        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        RedisTemplate template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        template.setKeySerializer(jackson2JsonRedisSerializer);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashKeySerializer(jackson2JsonRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

    @Bean
    @ConditionalOnMissingBean(StringRedisTemplate.class)
    public StringRedisTemplate stringRedisTemplate(
            RedisConnectionFactory redisConnectionFactory)
            throws UnknownHostException {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}

Redis 的配置就完成了。

4.2 Redis 的數(shù)據(jù)結(jié)構(gòu)類型

Redis 可以存儲鍵與5種不同數(shù)據(jù)結(jié)構(gòu)類型之間的映射,這5種數(shù)據(jù)結(jié)構(gòu)類型分別為String(字符串)、List(列表)、Set(集合)、Hash(散列)和 Zset(有序集合)。

下面來對這5種數(shù)據(jù)結(jié)構(gòu)類型作簡單的介紹:

結(jié)構(gòu)類型 結(jié)構(gòu)存儲的值 結(jié)構(gòu)的讀寫能力
String 可以是字符串、整數(shù)或者浮點(diǎn)數(shù) 對整個字符串或者字符串的其中一部分執(zhí)行操作;對象和浮點(diǎn)數(shù)執(zhí)行自增(increment)或者自減(decrement)
List 一個鏈表,鏈表上的每個節(jié)點(diǎn)都包含了一個字符串 從鏈表的兩端推入或者彈出元素;根據(jù)偏移量對鏈表進(jìn)行修剪(trim);讀取單個或者多個元素;根據(jù)值來查找或者移除元素
Set 包含字符串的無序收集器(unorderedcollection),并且被包含的每個字符串都是獨(dú)一無二的、各不相同 添加、獲取、移除單個元素;檢查一個元素是否存在于某個集合中;計(jì)算交集、并集、差集;從集合里賣弄隨機(jī)獲取元素
Hash 包含鍵值對的無序散列表 添加、獲取、移除單個鍵值對;獲取所有鍵值對
Zset 字符串成員(member)與浮點(diǎn)數(shù)分值(score)之間的有序映射,元素的排列順序由分值的大小決定 添加、獲取、刪除單個元素;根據(jù)分值范圍(range)或者成員來獲取元素
4.3 StringRedisTemplate 與 RedisTemplate

RedisTemplate 對五種數(shù)據(jù)結(jié)構(gòu)分別定義了操作

redisTemplate.opsForValue();

操作字符串

redisTemplate.opsForHash();

操作hash

redisTemplate.opsForList();

操作list

redisTemplate.opsForSet();

操作set

redisTemplate.opsForZSet();

操作有序set

如果操作字符串的話,建議用 StringRedisTemplate 。

StringRedisTemplate 與 RedisTemplate 的區(qū)別

StringRedisTemplate 繼承了 RedisTemplate。

RedisTemplate 是一個泛型類,而 StringRedisTemplate 則不是。

StringRedisTemplate 只能對 key=String,value=String 的鍵值對進(jìn)行操作,RedisTemplate 可以對任何類型的 key-value 鍵值對操作。

他們各自序列化的方式不同,但最終都是得到了一個字節(jié)數(shù)組,殊途同歸,StringRedisTemplate 使用的是 StringRedisSerializer 類;RedisTemplate 使用的是 JdkSerializationRedisSerializer 類。反序列化,則是一個得到 String,一個得到 Object

兩者的數(shù)據(jù)是不共通的,StringRedisTemplate 只能管理 StringRedisTemplate 里面的數(shù)據(jù),RedisTemplate 只能管理 RedisTemplate中 的數(shù)據(jù)。

4.4 項(xiàng)目中使用

在需要使用 Redis 的地方,用 @Autowired 注入進(jìn)來

@Autowired
RedisTemplate redisTemplate;
 
@Autowired
StringRedisTemplate stringRedisTemplate;

由于項(xiàng)目中暫時僅用到了 StringRedisTemplate 與 RedisTemplate 的 Hash 結(jié)構(gòu),StringRedisTemplate 比較簡單就不貼代碼了,下面僅對操作 Hash 進(jìn)行舉例。

關(guān)于 RedisTemplate 的詳細(xì)用法,有一篇文章已經(jīng)講的很細(xì)很好了,我覺得沒必要再去寫了。傳送門

用 RedisTemplate 操作 Hash
package com.solo.coderiver.user.service.impl;

import com.solo.coderiver.user.dataobject.UserLike;
import com.solo.coderiver.user.dto.LikedCountDTO;
import com.solo.coderiver.user.enums.LikedStatusEnum;
import com.solo.coderiver.user.service.LikedService;
import com.solo.coderiver.user.service.RedisService;
import com.solo.coderiver.user.utils.RedisKeyUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class RedisServiceImpl implements RedisService {

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    LikedService likedService;

    @Override
    public void saveLiked2Redis(String likedUserId, String likedPostId) {
        String key = RedisKeyUtils.getLikedKey(likedUserId, likedPostId);
        redisTemplate.opsForHash().put(RedisKeyUtils.MAP_KEY_USER_LIKED, key, LikedStatusEnum.LIKE.getCode());
    }

    @Override
    public void unlikeFromRedis(String likedUserId, String likedPostId) {
        String key = RedisKeyUtils.getLikedKey(likedUserId, likedPostId);
        redisTemplate.opsForHash().put(RedisKeyUtils.MAP_KEY_USER_LIKED, key, LikedStatusEnum.UNLIKE.getCode());
    }

    @Override
    public void deleteLikedFromRedis(String likedUserId, String likedPostId) {
        String key = RedisKeyUtils.getLikedKey(likedUserId, likedPostId);
        redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_KEY_USER_LIKED, key);
    }

    @Override
    public void incrementLikedCount(String likedUserId) {
        redisTemplate.opsForHash().increment(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, likedUserId, 1);
    }

    @Override
    public void decrementLikedCount(String likedUserId) {
        redisTemplate.opsForHash().increment(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, likedUserId, -1);
    }

    @Override
    public List getLikedDataFromRedis() {
        Cursor> cursor = redisTemplate.opsForHash().scan(RedisKeyUtils.MAP_KEY_USER_LIKED, ScanOptions.NONE);
        List list = new ArrayList<>();
        while (cursor.hasNext()) {
            Map.Entry entry = cursor.next();
            String key = (String) entry.getKey();
            //分離出 likedUserId,likedPostId
            String[] split = key.split("::");
            String likedUserId = split[0];
            String likedPostId = split[1];
            Integer value = (Integer) entry.getValue();

            //組裝成 UserLike 對象
            UserLike userLike = new UserLike(likedUserId, likedPostId, value);
            list.add(userLike);

            //存到 list 后從 Redis 中刪除
            redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_KEY_USER_LIKED, key);
        }

        return list;
    }

    @Override
    public List getLikedCountFromRedis() {
        Cursor> cursor = redisTemplate.opsForHash().scan(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, ScanOptions.NONE);
        List list = new ArrayList<>();
        while (cursor.hasNext()) {
            Map.Entry map = cursor.next();
            //將點(diǎn)贊數(shù)量存儲在 LikedCountDT
            String key = (String) map.getKey();
            LikedCountDTO dto = new LikedCountDTO(key, (Integer) map.getValue());
            list.add(dto);
            //從Redis中刪除這條記錄
            redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, key);
        }
        return list;
    }
}
五、Redis 實(shí)現(xiàn)分布式鎖

講完了基礎(chǔ)操作,再說個實(shí)戰(zhàn)運(yùn)用,用Redis 實(shí)現(xiàn)分布式鎖 。

實(shí)現(xiàn)分布式鎖之前先看兩個 Redis 命令:

SETNX

key設(shè)置值為value,如果key不存在,這種情況下等同SET命令。 當(dāng)key存在時,什么也不做。SETNX是”SET if Not eXists”的簡寫。

返回值

Integer reply, 特定值:

1 如果key被設(shè)置了

0 如果key沒有被設(shè)置

例子

redis> SETNX mykey "Hello"
(integer) 1
redis> SETNX mykey "World"
(integer) 0
redis> GET mykey
"Hello"
redis> 

GETSET

自動將key對應(yīng)到value并且返回原來key對應(yīng)的value。如果key存在但是對應(yīng)的value不是字符串,就返回錯誤。

設(shè)計(jì)模式

GETSET可以和INCR一起使用實(shí)現(xiàn)支持重置的計(jì)數(shù)功能。舉個例子:每當(dāng)有事件發(fā)生的時候,一段程序都會調(diào)用INCR給key mycounter加1,但是有時我們需要獲取計(jì)數(shù)器的值,并且自動將其重置為0。這可以通過GETSET mycounter “0”來實(shí)現(xiàn):

INCR mycounter
GETSET mycounter "0"
GET mycounter

返回值

bulk-string-reply: 返回之前的舊值,如果之前Key不存在將返回nil。

例子

redis> INCR mycounter
(integer) 1
redis> GETSET mycounter "0"
"1"
redis> GET mycounter
"0"
redis>

這兩個命令在 java 中對應(yīng)為 setIfAbsentgetAndSet

分布式鎖的實(shí)現(xiàn):

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

@Component
@Slf4j
public class RedisLock {

    @Autowired
    StringRedisTemplate redisTemplate;

    /**
     * 加鎖
     * @param key
     * @param value 當(dāng)前時間 + 超時時間
     * @return
     */
    public boolean lock(String key, String value){
        if (redisTemplate.opsForValue().setIfAbsent(key, value)){
            return true;
        }

        //解決死鎖,且當(dāng)多個線程同時來時,只會讓一個線程拿到鎖
        String currentValue = redisTemplate.opsForValue().get(key);
        //如果過期
        if (!StringUtils.isEmpty(currentValue) &&
                Long.parseLong(currentValue) < System.currentTimeMillis()){
            //獲取上一個鎖的時間
            String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
            if (StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)){
                return true;
            }
        }

        return false;
    }

    /**
     * 解鎖
     * @param key
     * @param value
     */
    public void unlock(String key, String value){

        try {
            String currentValue = redisTemplate.opsForValue().get(key);
            if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)){
                redisTemplate.opsForValue().getOperations().delete(key);
            }
        }catch (Exception e){
            log.error("【redis鎖】解鎖失敗, {}", e);
        }
    }
}

使用:

/**
 * 模擬秒殺
 */
public class SecKillService {

    @Autowired
    RedisLock redisLock;

    //超時時間10s
    private static final int TIMEOUT = 10 * 1000;

    public void secKill(String productId){
        long time = System.currentTimeMillis() + TIMEOUT;
        //加鎖
        if (!redisLock.lock(productId, String.valueOf(time))){
            throw new SellException(101, "人太多了,等會兒再試吧~");
        }

        //具體的秒殺邏輯

        //解鎖
        redisLock.unlock(productId, String.valueOf(time));
    }
}

更多 Redis 的具體使用場景請關(guān)注開源項(xiàng)目 CodeRiver,致力于打造全平臺型全棧精品開源項(xiàng)目。

coderiver 中文名 河碼,是一個為程序員和設(shè)計(jì)師提供項(xiàng)目協(xié)作的平臺。無論你是前端、后端、移動端開發(fā)人員,或是設(shè)計(jì)師、產(chǎn)品經(jīng)理,都可以在平臺上發(fā)布項(xiàng)目,與志同道合的小伙伴一起協(xié)作完成項(xiàng)目。

coderiver河碼 類似程序員客棧,但主要目的是方便各細(xì)分領(lǐng)域人才之間技術(shù)交流,共同成長,多人協(xié)作完成項(xiàng)目。暫不涉及金錢交易。

計(jì)劃做成包含 pc端(Vue、React)、移動H5(Vue、React)、ReactNative混合開發(fā)、Android原生、微信小程序、java后端的全平臺型全棧項(xiàng)目,歡迎關(guān)注。

項(xiàng)目地址:https://github.com/cachecats/...

您的鼓勵是我前行最大的動力,歡迎點(diǎn)贊,歡迎送小星星? ~

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/72271.html

相關(guān)文章

  • springboot 整合redis

    摘要:與整合默認(rèn)使用的是,相較于,是一個可伸縮的,線程安全的客戶端。在處理高并發(fā)方面有更多的優(yōu)勢。使用依賴主要需要的依賴為配置配置使用與整合可以在不更改現(xiàn)有代碼邏輯的基礎(chǔ)上,通過增加注解的方式,實(shí)現(xiàn)緩存。 springboot2.0 與redis整合默認(rèn)使用的是Lettuce,相較于jedis,lettuce 是一個可伸縮的,線程安全的redis客戶端。在處理高并發(fā)方面有更多的優(yōu)勢。 Red...

    elarity 評論0 收藏0
  • springboot系列】springboot整合獨(dú)立模塊 redis 做緩存

    摘要:至此,已完成整合獨(dú)立模塊做緩存詳情請看地址相關(guān)文章系列整合獨(dú)立模塊 項(xiàng)目github地址:https://github.com/5-Ason/aso...具體可看 ./db/db-redis 和 ./db/db-cache 兩個模塊 // TODO 在整合redis之前需要先本地配置好redis環(huán)境,遲點(diǎn)有時間補(bǔ)一下linux下下載安裝配置redis 本文主要實(shí)現(xiàn)的是對數(shù)據(jù)操作進(jìn)行獨(dú)立...

    qianfeng 評論0 收藏0
  • springboot系列】springboot整合獨(dú)立模塊 redis 做緩存

    摘要:至此,已完成整合獨(dú)立模塊做緩存詳情請看地址相關(guān)文章系列整合獨(dú)立模塊 項(xiàng)目github地址:https://github.com/5-Ason/aso...具體可看 ./db/db-redis 和 ./db/db-cache 兩個模塊 // TODO 在整合redis之前需要先本地配置好redis環(huán)境,遲點(diǎn)有時間補(bǔ)一下linux下下載安裝配置redis 本文主要實(shí)現(xiàn)的是對數(shù)據(jù)操作進(jìn)行獨(dú)立...

    Jokcy 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<