redis

面试有时候会问道如何自己实现分布式锁

定义一个锁接口,定义两个抽象tryLock和unLock;

通过一个类实现这个接口。

基础版:

 @Override
    public boolean tryLock(long timeOutSec) {
        //获取锁
        Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(
                KEY_PREFIX + name, threadId + "", timeOutSec, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(success);
    }

    @Override
    public void unLock() {
            stringRedisTemplate.delete(KEY_PREFIX + name);
    }

为了防止出现锁误删进行修改。

在获取锁时存入线程标识,通过UUID,释放锁时获取锁中标识是否与线程标识一致,如果一致就可以释放锁。

public class SimpleRedisLock implements ILock {
    private StringRedisTemplate stringRedisTemplate;
    private String name;
    private static final String KEY_PREFIX = "lock:";
    private static final String ID_PREFIX = UUID.randomUUID().toString(true) + "-";

    @Override
    public boolean tryLock(long timeOutSec) {
        //获取线程标识。
        long threadId = Thread.currentThread().getId();
        //获取锁
        Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(
                KEY_PREFIX + name, threadId + "", timeOutSec, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(success);
    }

    @Override
    public void unLock() {
        String threadId = ID_PREFIX + Thread.currentThread();
        String ID = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);
        //判断标识是否一致,
        if(threadId.equals(ID)){
            stringRedisTemplate.delete(KEY_PREFIX + name);
        }
    }
}

但是对于锁判断和锁释放并不是一个原子性的操作,仍然会存在一些问题。继续进行改进.

static {
        UNLOCK_SCRIPT = new DefaultRedisScript<>();
        UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));
        UNLOCK_SCRIPT.setResultType(Long.class);
    }

    @Override
    public boolean tryLock(long timeOutSec) {
        //获取线程标识。
        String threadId = KEY_PREFIX + Thread.currentThread().getId();
        //获取锁
        Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(
                KEY_PREFIX + name, threadId, timeOutSec, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(success);
    }

    @Override
    public void unLock() {

        stringRedisTemplate.execute(UNLOCK_SCRIPT,
                Collections.singletonList(KEY_PREFIX + name),
                KEY_PREFIX + Thread.currentThread().getId());
    }

2.redis持久化
RDB方式:

使用bgsave,异步执行,fork主进程执行一个子进程共享内存。子进程读取内存存数据写入新的RDB文件,用新的RDB文件替换旧的RDB文件。

(fork采用copy-on-write,当主进程执行读操作,访问共享内存,主进程执行写操作时,会会拷贝一份数据,执行写操作)。

缺点:RDB执行周期较长,两次RDB之间会有数据丢失的风险

fork子进程、压缩、写出RDB文件都比较耗时。

AOF方式:

redis处理的每一个写命令操作都会记录在AOF文件,可以看作时命令日志文件。

有三种方式:
always :同步刷盘,可靠性高,基本不会丢失数据,性能影响最大。

everysec(默认):每秒刷盘,性能适中,最多丢失一秒的数据。

no:执行命令写入AOF缓冲区,由操作系统决定何时将缓冲区内容写入磁盘。

缺点:记录过多写操作造成冗余。通过执行bgrewriteaof命令,可以让AOF文件执行重写功能,用最少的命令达到相同效果。

 3.RDB和AOF的区别:

 4.Redis的数据结构?

对于String类型的字符串:

 基本的编码方式是RAW,基于简单动态字符串SDS实现,存储上限是512mb。

SDS是C语言中的结构体,包含了属性,len,alloc,flag(不同sds头类型),char buf[]。

        如果存储的SDS长度小于44字节(抛出redisObject的),则会采用EMBSTR编码,此时object head与SDS是一段连续空间。申请内存时只需要调用一次内存分配函数,效率更高。        如果存储的字符串是整数值,并且大小在LONG_MAX范围内,则会采用INT编码:直接将数据保存在RedisObject的ptr指针位置(刚好8字节),不再需要SDS了。

对于Zset的底层数据结构:

满足键值存储,键必须唯一,可排序。

redisObject的ptr指向一个Zset对象,Zset对象中保存了两个指针,一个*dic字典,一个*zsl跳表,字典负责查找,跳表负责排序。
缺点是保存了两份数据,浪费了内存空间。

简述skipList:
双向链表,数值按升序排列,节点有多个指针,指针的跨度不同。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值