分布式锁浅谈

结合redis和redisson依赖设计的分布式实现,达到读写互斥,且避免了缓存穿透和雪崩

涉及的依赖


        <!--json依赖-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.4</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.9.4</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.9.4</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>


        <!-- Spring AOP依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
        </dependency>
        <!-- Spring 事务管理依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
        </dependency>
        <!-- 如果你使用AspectJ的注解方式,还需要添加AspectJ的依赖 -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
        </dependency>
        <!--redisson-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.16.2</version>
        </dependency>

代码实现

package com.chenling.redis;

import com.alibaba.fastjson.JSON;
import com.chenling.Util.RedisUtil;
import com.chenling.dao.ProductDao;
import com.chenling.vo.Product;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

/**
 * @Desc: 分布式锁实现
 * @Auther:chenLing
 * @Date: 2024/8/9 10:43
 */
@Service
public class DclLock {
    static final String CACHE_PRODUCT = "cache:product:";
    static final String CACHE_PRODUCT_UPDATE = "cache:product:update:";
    static final String PRODUCT_EMPTY = "-1";

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private Redisson redisson;
    @Autowired
    private ProductDao productDao;

    public Product get(Long productId) {
        Product product = null;
        String cacheKey = CACHE_PRODUCT + productId;
        product = getProductFromCache(cacheKey);
        if (product != null) {
            return product;
        }
        // DCL
        RLock getLock = redisson.getLock(cacheKey);
        // 加锁
        getLock.lock();
        try {
            // 因为已经加锁,所以并发时的查询到此会变成串行,此时第一个的查询结果会缓存到数据库,所以再查一次缓存,
            product = getProductFromCache(cacheKey);
            if (product != null) {
                return product;
            }
            // 增加写锁,避免查询和更新并发,造成数据的不一致
            RReadWriteLock readWriteLock = redisson.getReadWriteLock(CACHE_PRODUCT_UPDATE + productId);
            RLock rLock = readWriteLock.readLock();
            rLock.lock();            try {
                product = productDao.get(productId);
                if (product == null) {
                    // 空值设置-1的缓存,避免缓存击穿
                    redisUtil.set(cacheKey, PRODUCT_EMPTY, 100);
                } else {
                    // 查到则保存缓存,建议缓存时间设置一个具体时间的随机时间,避免同一时间缓存大量失效,造成缓存雪崩
                    redisUtil.set(cacheKey, JSON.toJSONString(product), 1000);
                }
            } finally {
                rLock.unlock();
            }
        } finally {
            getLock.unlock();
        }
        return product;
    }

    @Transactional
    public Product update(Product product) {
        // 更新前上锁,如果此锁已经在查询,则更新滞后
        RReadWriteLock readWriteLock = redisson.getReadWriteLock(CACHE_PRODUCT_UPDATE + product.getProductId());
        RLock updateLock = readWriteLock.writeLock();
        updateLock.lock();
        try {
            productDao.update(product);
            redisUtil.set(CACHE_PRODUCT + product.getProductId(), JSON.toJSONString(product), 1000);
        } finally {
            updateLock.unlock();
        }
        return new Product();
    }

    public Product getProductFromCache(String cacheKey) {
        String productStr = redisUtil.get(cacheKey);
        if (!StringUtils.hasLength(productStr)) {
            if (productStr.equals(PRODUCT_EMPTY)) {
                return new Product();
            } else {
                return JSON.parseObject(productStr, Product.class);
            }
        }
        return null;
    }
}

代码到这里初步实现了读写互斥,但是锁加的过多,且,在缓存不存在而多个读并发时,并发的每个 读都需要加一次锁,存在锁重复问题,有继续优化的空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值