String类型的命令

引用

本文主要介绍redis的string类型的命令。主要包括以下几个

  • set key value [ex second | px mills] [nx | xx]
  • get key
  • del key
  • expire key second
  • strlen key
  • setrange key index value
  • getrange key low high
  • mset key value key2 value2
  • msetnx key value key2 value2
  • append key value
  • incr key
  • incrby key value
  • decr key
  • decrby key value
  • getset key resetvalue

挨个介绍

记住redis的key都是string类型

设置一个string类型的value

set key value [ex second | px mills] [nx | xx]

  • ex是设置过期时间 单位是s
  • px也是过期时间 单位是ms
  • nx是不存在才执行命令
  • xx是存在才执行命令

获取一个string类型的值

get key

删除一个string类型的值

del key

对所有类型的值都有效
设置一个键的过期时间,单位是s

expire key second

获取string类型值的长度
我试了下一个中文字符的长度3

strlen key

对一个string值从index开始填充value字符串。

setrange key index value`

获取一个string值的某一段 low和high是下标,获取的范围是[low,high]。会返回范围内出string值,不存在就返回空

getrange key low high

一次性设置多个key value对。这是一个原子操作

mset key value key2 value2

一次性设置多个key value,且只在key不存在的情况下。这也是一个原子操作。当有一个key设置失败时,本次所有key的设置都不生效

msetnx key value key2 value2

对一个string类型的number自增1,如果key不存在那么先设置一个0的key字符串。再自增1.这是原子操作

incr key

与incr相同,但是自增的步长可以自己指定

incrby key step

与incr类型,对自减1

decr key

与incrby类型, 值是自减的

decrby key step

对指定key的字符串末尾添加一个字符串。如果key不存在就相当于set命令

append key value

获取一个key的值,并且覆盖这个key的值

getset key resetvalue

案例一 分布式锁

redis的CAP模型中满足的是CP模型,但是本身分布式锁要求的是强一致性是一个CP模型。所有使用redis做分布式锁是不推荐的,但是小规模、不要求强一致性的情况下也是能快速实现分布式锁的一种方式。下面使用java代码实现一个简单版的分布式锁。

乞丐版

通过sexnx这个命令获取锁。
释放锁的时候就比较讲究了。因为线程执行完后释放锁的时候,可能锁已经过期了,此时需要使用lua脚本删除锁,保证只删掉自己的锁。lua脚本是为了保证原子性。

此版本存在的问题有:

  • 直接把uuid返回了,这样不符合设计模式,需要使用面对对象的法则,返回锁对象。提供release方法
  • 不支持重入,多次获取自己的锁会失败
  • 没有控制锁释放的权限,其他线程知道uuid也能把锁释放了。
  • 锁的释放也需要支持重复,且需要返回正确的结果。
@Component
public class RedisDistributeLock {

    private static RedisTemplate<String, String> redisTemplate;

    public static String getLock(String key,int expire) throws RuntimeException {
        String uniq = UUID.randomUUID().toString();
        Boolean result = redisTemplate.opsForValue()
            .setIfAbsent(key, uniq, expire, TimeUnit.SECONDS);
        if (null == result || !result) {
            throw new RuntimeException();
        }
        return uniq;
    }

    public static Boolean release(String key, String uniq) {
        return redisTemplate.execute(
            (RedisCallback<Boolean>) connection -> connection.scriptingCommands()
                .eval(("if redis.call('get',KEYS[1]) == "
                        + "ARGV[1] "
                        + "then "
                        + "return redis.call('del',KEYS[1]) "
                        + "else return 0 end").getBytes(), ReturnType.BOOLEAN,
                    1,
                    key.getBytes() , uniq.getBytes()));
    }

    @Autowired
    public void setRedisTemplate(
        RedisTemplate<String, String> redisTemplate) {
        RedisDistributeLock.redisTemplate = redisTemplate;
    }
}

平民版

  • 把uuid存入了线程本地变量,解决了直接返回给用户这一不合理的设计
  • 支持了重入
  • 控制锁释放的权限

这个版本的问题在于没有实现等待且超时的机制,没有满足客户端期望尝试一段时间内去获取锁


/**
 * @author djcao
 * @workcode BG389966
 * @date 2020/6/4
 */
@Component
public class RedisDLock implements DistributeLock
{
    ThreadLocal<Map<String,String>> threadLocal = ThreadLocal.withInitial(HashMap::new);
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public Boolean lock(String key, Integer timeout, TimeUnit timeUnit) {
        String lockedValue = threadLocal.get().get(key);
        if (!StringUtils.isEmpty(lockedValue)) {
            return true;
        }
        String value = UUID.randomUUID().toString();
        Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, timeout, timeUnit);
        if (result != null && result) {
            threadLocal.get().put(key, value);
        }
        return result;
    }

    @Override
    public Boolean unlock(String key) {
        String value = threadLocal.get().get(key);
        if (value == null) {
            return true;
        }
        redisTemplate.execute((RedisCallback<Boolean>) connection -> connection.scriptingCommands().eval(("if redis.call('get', KEYS[1] ) == "
                + "ARGV[1] then return redis.call('del',KEYS[1]) else 0 end").getBytes(),
            ReturnType.BOOLEAN,1,
            key.getBytes(),threadLocal.get().get(key).getBytes()));
        threadLocal.get().remove(key);
        return true;
    }
}

土豪版

需要使用到后面的watch机制。
思路如下
如果设置不成功,那么对一个key进行watch,当key被删除后再尝试获取。

案例二 限速器

我们可以使用限速器有效解决恶意用户和爬虫频繁调用接口的问题。

/**
 * @author djcao
 * @workcode BG389966
 * @date 2020/6/6
 */
@Component
public class RedisLimit {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 限制QPS
     *
     * @param ip    key
     * @param limit limit
     * @return
     */
    public Boolean entry(String ip, Long limit) {
        if (BooleanUtils.isTrue(redisTemplate.hasKey(ip))) {
            Boolean result = redisTemplate.opsForValue()
                .setIfAbsent(ip, "1", 1, TimeUnit.SECONDS);
            if (result == null || !result) {
                return incr(ip) > limit;
            }
            return result;
        }
        return incr(ip) > limit;
    }

    private Long incr(String key) {
        return redisTemplate.opsForValue().increment(key);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值