Redis零散知识

redis的单线程

网络IO和键值对读写是单线程,6.0后网络IO多线程,键值对读写仍然是单线程。

redis快的原因

1、基于内存

2、网络请求和键值对读写单线程,避免线程切换的开销避免加锁

3、数据结构丰富

redis应用场景:

1、做热点数据的缓存。

2、登陆分布式session缓存token

2、点赞统计,排行

2、分布式锁。

3、断电丢失数据可以的话,可以当数据库。

redis缓存一致性

这里说的是redis和mysql的一致性。

更新操作有两个问题:缓存是直接删掉还是更新,先更新数据库数据还是先删缓存。

更新数据的时 缓存是直接删掉还是更新​​​​​​​​​​​​​​

1、直接删掉缓存,等下一次的读操作把数据库的数据同步到缓存。(第一次读不会命中缓存)

2、更新数据库时,顺便把缓存也更新了。(更新了却不读白白消耗了时间和性能)

如果写很多,读却很少,那么第2种方式就做了白白做了很多更新缓存的操作。

如果读很多,那么第一种方式不能命中缓存,又要做同步的操作。

建议直接删掉缓存。

更新数据的时,用删掉缓存的方式,先更新数据库数据还是先删缓存

1、先操作数据库,数据库更新完,在删掉缓存前,读操作会短暂的命中缓存,读到更新前的数据。(短暂的不一致影响小

2、更新线程先删掉缓存,在更新数据库还没提交,这时候来一个读线程很快的把旧数据同步回缓存, 直到下一个写线程来之前读线程都会命中缓存的旧数据。(影响大)

建议先更新数据库后删除缓存

如果想硬用先删缓存的方式可以用延迟双删

1、 删除缓存key

2、 更新数据库

3、 睡眠一个合适的时间

4、再次删掉这个key(尽管有读线程很快同步回缓存了旧数据也会被删掉,后面就不会读旧的数据,但是还是推荐先操作数据库)

//写操作
public void write(String key, Object data) {
    Redis.delKey(key);       //1、先删除缓存
    MySQL.updateData(data);  //2、在更新mysql
    Thread.sleep(100);       //3、睡眠一个合适的时间
    Redis.delKey(key);       //4、在删掉可能被读取同步回缓存的旧数据
}

如果数据库是读写分离的怎么办

可以用中间件读取主库bin log。然后在对这个数据的修改去把redis缓存主动更新下。

redis分布式锁

为什么要做分布式锁?

如果一个应用不是单点的,而是分布式部署,那么每个JVM的实例会有各自的内存,锁住的也是各自实例的锁,也就失效了。

只有redis能做吗?

不仅redis能做,Zookeeper和MySQL也能做。

redis做分布式锁注意什么?

1、原子性:加锁、解锁一定要保证原子性:(原子API)

2、不能死锁:给锁一个过期时间,保证这个锁一定能释放:(设置过期时间)

3、加锁与解锁应该是同一个人:(解锁判断valueID)

4、过期时间到了会释放锁,但是加锁的人没执行完,看门狗续key过期时间(watch dog)

redis做分布式锁的实现方式?

加锁:

key是锁,value存的客户端ID,如果这key不存在才设置这个key,并且设置过期时间

public class RedisTool {
     
    //加锁是否成功
    private static final String LOCK_SUCCESS = "OK";
    //不存在既上锁,存在即不做操作。
    private static final String SET_IF_NOT_EXIST = "NX";
    //设置过期时间
    private static final String SET_WITH_EXPIRE_TIME = "PX";
 
    /**
     * 尝试获取分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @param expireTime 超期时间
     * @return 是否获取成功
     */
    public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
        //jedis的原子操作API
        String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
 
        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }
        return false;
 
    }
 
}

解锁:

首先获取锁对应的value值,检查是否与requestId相等,如果相等才是加锁的客户端来解锁(删除key)

public class RedisTool {
 
    private static final Long RELEASE_SUCCESS = 1L;
 
    /**
     * 释放分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
 
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        //因为解锁也要保证原子,所以用eval方法交给redis服务端执行
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
 
        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;
 
    }
 
}

补充:

多机Redis用Redisson(我没用过)Redisson好像有现成的API 。lock、unlock。

watch dog:如果我过期时间10s,但是程序执行了15s,我还没执行完的时候这样别人可以拿到锁,需要我自己过期前续10s。

MySQL分布式锁:利用主键,或者唯一索引唯一性互斥。谁成功添加了这个ID谁拿到锁。

redis事务

redis事务没有原子性:是把一串命令执行,事务里命令执行成功了的命令就是成功了,执行失败的命令就是失败了。不会因为事务里某条命令的失败而回滚。

MULTI:开启事务

EXEC:提交事务

DISCARD:放弃事务

WATCH:监控一个或多个key。key在(EXEC)之前被其他用户修改过,重新获取最新数据操作

UNWATCH:取消监控

redis事务很鸡肋,redis发布订阅也很鸡肋

redis持久化RDB和AOF

RDB全量备份:(故障丢失数据、恢复数据快、体积小)

AOF命令追加:(故障丢失最多修饰一条命令,有命令重写,恢复数据慢相当于在执行一遍)

(4.0之后)混合持久化:在重写策略上做优化重写的时,把当前内存数据以rdb形式放到到aof文件头部,重写完之后的写命令用增量日志放到aof文件尾,以此往复。这样RDB恢复快,增量数据AOF也完整。

AOF追加策略:

always: 每写入一个命令都追加一次。

everysec: 每一秒都追加一次(默认)。

no: 操作系统自己决定什么时候追加。

1redis有什么操作,

能对永久的键设置过期时间

固定过期,当前设置多久后过期两种

能判断某个键存不存在

String有incr和decr

设置键的过期时间:expire key seconds

能得到key的剩余生存时间:ttl key

删除一个或者多个,key不存在会忽略不会报错正常执行  del key [key…]

String:

理解几个字母的意思

m是多个

nx用的时候键必须不存在

xx键必须存在

设置一个键且赋值,键如果存在会覆盖值:set key value

可以追加,这个命令如果key不存在也不会报错,直接创建这个key:append key value

返回字符串的长度 key不存在没关系:strlen key

可以同时设置一个key value或者多对key value:mset key value [key value…]

可以一次性获取一个或多个值:mget key [key …]

2、 不常用操作有:

2用PAIClinet应该注意什么

用1.8try()的括号释放连接,但是要实现CloseAble接口的close方法

Jedis是否只支持单节点(redis单节点)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值