【分布式锁】redis设置分布式锁,redission设置分布式锁,常见面试题

文章目录

  • 分布式锁
    • 一、redis设置分布式锁
      • 1. 基本原理和实现方式对比
    • 二、redission设置分布式锁
      • 1. 分布式锁-redission功能介绍
      • 2. 引入依赖
      • 3. 配置Redisson客户端
      • 4. 使用Redission的分布式锁
    • 三、redis常见的面试题
      • 1. redis支持的数据类型。常见的有五种
      • 2. redis持久化方式。
      • 3. redis如何解决单机故障。---搭建redis集群。
      • 4. redis淘汰策略。
      • 5. redis如何实现分布式锁。
      • 6. redis实现分布式锁的缺陷。
      • 7. redis的使用场景
      • 8. 有没有碰到缓存穿透,如何解决缓存穿透。

分布式锁

一、redis设置分布式锁

1. 基本原理和实现方式对比

分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。

分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行执行,这就是分布式锁的核心思路

【分布式锁】redis设置分布式锁,redission设置分布式锁,常见面试题_第1张图片

那么分布式锁他应该满足一些什么样的条件呢?

  • 可见性:多个线程都能看到相同的结果,注意:这个地方说的可见性并不是并发编程中指的内存可见性,只是说多个进程之间都能感知到变化的意思
  • 互斥:互斥是分布式锁的最基本的条件,使得程序串行执行
  • 高可用:程序不易崩溃,时时刻刻都保证较高的可用性
  • 高性能:由于加锁本身就让性能降低,所有对于分布式锁本身需要他就较高的加锁性能和释放锁性能
  • 安全性:安全也是程序中必不可少的一环

核心思路:
我们利用redis 的setNx 方法,当有多个线程进入时,我们就利用该方法,第一个线程进入时,redis 中就有这个key 了,返回了1,如果结果是1,则表示他抢到了锁,那么他去执行业务,然后再删除锁,退出锁逻辑,没有抢到锁的哥们,等待一定时间后重试即可

二、redission设置分布式锁

1. 分布式锁-redission功能介绍

基于setnx实现的分布式锁存在下面的问题:

重入问题:重入问题是指 获得锁的线程可以再次进入到相同的锁的代码块中,可重入锁的意义在于防止死锁,比如HashTable这样的代码中,他的方法都是使用synchronized修饰的,假如他在一个方法内,调用另一个方法,那么此时如果是不可重入的,不就死锁了吗?所以可重入锁他的主要意义是防止死锁,我们的synchronized和Lock锁都是可重入的。

不可重试:是指目前的分布式只能尝试一次,我们认为合理的情况是:当线程在获得锁失败后,他应该能再次尝试获得锁。

超时释放: 我们在加锁时增加了过期时间,这样的我们可以防止死锁,但是如果卡顿的时间超长,虽然我们采用了lua表达式防止删锁的时候,误删别人的锁,但是毕竟没有锁住,有安全隐患

主从一致性: 如果Redis提供了主从集群,当我们向集群写数据时,主机需要异步的将数据同步给从机,而万一在同步过去之前,主机宕机了,就会出现死锁问题。
【分布式锁】redis设置分布式锁,redission设置分布式锁,常见面试题_第2张图片

那么什么是Redission呢
Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务,其中就包含了各种分布式锁的实现。

Redission提供了分布式锁的多种多样的功能
【分布式锁】redis设置分布式锁,redission设置分布式锁,常见面试题_第3张图片

2. 引入依赖

<dependency>
    <groupId>org.redissongroupId>
    <artifactId>redissonartifactId>
    <version>3.13.4version>
dependency>

3. 配置Redisson客户端

@Configuration
public class RedissonConfig {
    @Bean
    public Redisson redisson(){
        Config config =new Config();
        config.useSingleServer()
                .setAddress("redis://192.168.179.130:6379")
                .setPassword("123456")
                //redis默认有16个数据库
                .setDatabase(0);
        return (Redisson) Redisson.create(config);
    }
}

4. 使用Redission的分布式锁

package com.aaa.service.impl;

import com.aaa.domain.Product;
import com.aaa.mapper.ProductMapper;
import com.aaa.service.ProductService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * @author : 尚腾飞([email protected])
 * @version : 1.0
 * @createTime : 2022/10/27 23:17
 * @description :
 */
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {
    @Autowired
    private ProductMapper productMapper;
    @Autowired
    private Redisson redisson;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Integer buyById(int id) {
        //1.使用redis设置分布式锁
        /*ValueOperations ops = stringRedisTemplate.opsForValue();

        Boolean isLock = ops.setIfAbsent("product:" + id, "", 5, TimeUnit.SECONDS);
        if (Boolean.FALSE.equals(isLock)) {
            System.out.println("服务器正忙...");
            return 1111;
        }*/

        //1.使用redisson设置分布式锁
        RLock lock = redisson.getLock("product:" + id);
        try {
            //2.使用redisson设置分布式锁
            lock.lock(30, TimeUnit.SECONDS);
            Product product = productMapper.selectById(id);
            Integer num = product.getNum();
            if (num > 0) {
                productMapper.buyById(id);
                System.out.println("库存剩余" + (num - 1));
            } else {
                System.out.println("库存不足!!");
            }
            return num - 1;
        } finally {
            //3.使用redisson设置分布式锁
            lock.unlock();

            //2.使用redis设置分布式锁
            //stringRedisTemplate.delete("product:" + id);
        }
    }
}

里面带有redis解决设置分布式锁的步骤,只需要把关于redisson的三个步骤注释掉,把redis的两个步骤放开就可以了。

三、redis常见的面试题

1. redis支持的数据类型。常见的有五种

常用:

  1. string字符串类型 2.hash 哈希类型 3.list队列类型 4.set集合类型 5.sort set有序集合。
    不常用:
  2. stream流 HyperLogLog超文本日志类型(估算) Bitmaps

2. redis持久化方式。

  • RDB:快照模式,在一定的时间间隔内,对redis数据进行快照存储。
  • AOF:日志追加。当我们执行写操作时,通过write函数把写命令追加到日志中。

3. redis如何解决单机故障。—搭建redis集群。

  1. 主从模式
  2. 哨兵模式
  3. 网状模式。(三主三从)

4. redis淘汰策略。

如果redis内存满足,在添加新的数据。

  1. volatile-lru:从已设置过期时间的数据集(server. db[i]. expires)中挑选最近最少使用的数据淘汰。
  2. volatile-ttl:从已设置过期时间的数据集(server. db[i]. expires)中挑选将要过期的数据淘汰。
  3. volatile-random:从已设置过期时间的数据集(server. db[i]. expires)中任意选择数据淘汰。
  4. allkeys-lru:从数据集(server. db[i]. dict)中挑选最近最少使用的数据淘汰。
  5. allkeys-random:从数据集(server. db[i]. dict)中任意选择数据淘汰。
  6. no-enviction(驱逐):禁止,驱逐数据

5. redis如何实现分布式锁。

我们利用redis 的setNx 方法,当有多个线程进入时,我们就利用该方法,第一个线程进入时,redis 中就有这个key 了,返回了1,如果结果是1,则表示他抢到了锁,那么他去执行业务,然后再删除锁,退出锁逻辑,没有抢到锁的哥们,等待一定时间后重试即可

6. redis实现分布式锁的缺陷。

Redis 分布式锁不能解决超时的问题,分布式锁有一个超时时间,程序的执行如果超出了锁的超时时间就会出现问题

7. redis的使用场景

  1. 作为缓存
  2. 可以完成分布锁
  3. 完成分布式session的共享。

8. 有没有碰到缓存穿透,如何解决缓存穿透。

  1. 什么是缓存穿透?
    所谓的缓存穿透就是在数据库中不存在该数据,缓存中也不存在该数据,这时有大量的请求,访问这种数据,这样就是把所有的请求交于数据库处理,造成数据库压力过大。从而导致数据库宕机。
  2. 如何解决缓存穿透。
  1. 缓存一个空对象。而且该对象设置的过期时间不能太长。一般不能超过5分钟。
  2. 设置布隆过滤器。它就是一个很多的bitMaps数组,里面存放了数据库表中所有数据的id. 如果查询的数据在bitmap中不存在,则该数据一定不会存在。则无需再查询数据库。
//布隆过滤器钟存储的是数据库表钟对应的id
String get(String key) {
 	//先从缓存获取。  
    String value = redis.get(key); 
    //缓存没有命中
    if (value  == null) {
    	//查看布隆过滤器钟是否存在
        if(!bloomfilter.mightContain(key)){
            return null; 
        }else{
        	//查询数据库
            value = db.get(key); 
            redis.set(key, value); 
        }    
    }
    return value;
}

你可能感兴趣的:(分布式,nosql,redis,分布式,java)