redis
自己整理,用到啥整理啥
五大数据类型
set
常用命令
sadd <key><value1><value2> ..... 将一个或多个 member 元素加入到集合 key 中,已经存在的 member 元素将被忽略
smembers <key> 取出该集合的所有值。
sismember <key><value> 判断集合<key>是否为含有该<value>值,有1,没有0
scard<key> 返回该集合的元素个数。
srem <key><value1><value2> .... 删除集合中的某个元素。
spop <key> 随机从该集合中吐出一个值。
srandmember <key><n> 随机从该集合中取出n个值。不会从集合中删除 。
smove <source><destination>value 把集合中一个值从一个集合移动到另一个集合
sinter <key1><key2> 返回两个集合的交集元素。
sunion <key1><key2>返回两个集合的并集元素。
sdiff <key1><key2>返回两个集合的差集元素(key1中的,不包含key2中的)
string
set <key><value>添加键值对
get <key>查询对应键值
append <key><value>将给定的<value> 追加到原值的末尾,返回字符串的长度
strlen <key>获得值的长度
setnx <key><value>只有在 key 不存在时 设置 key 的值
incr <key>
将 key 中储存的数字值增1
只能对数字值操作,如果为空,新增值为1
decr <key>
将 key 中储存的数字值减1
只能对数字值操作,如果为空,新增值为-1
incrby / decrby <key><步长>将 key 中储存的数字值增减。自定义步长。
解决秒杀问题
int uid = new Random().nextInt(10000);
// int uid = 1000;
SessionCallback<Object> callback = new SessionCallback<Object>() {
@Override
public List<Object> execute(RedisOperations operations) {
//先上乐观锁
operations.watch("commodity:1101");
//判断是否有货存
String s = redisTemplate.opsForValue().get("commodity:1101");
int anInt = Integer.parseInt(s);
if (anInt < 1) {
System.out.println("没货了");
return null;
}
//判断用户是否存在
Boolean member = redisTemplate.opsForSet().isMember("snapped:1101", uid + "");
if (member) {
System.out.println("用户已存在");
return null;
} else {
operations.multi();
operations.opsForValue().decrement("commodity:1101");
operations.opsForSet().add("snapped:1101", uid + "");
//运行
return operations.exec();
}
}
};
List execute = (List) redisTemplate.execute(callback);
if (execute != null && execute.size() > 0) {
System.out.println("秒杀成功");
}
System.out.println("秒杀失败");
return "";
需要注意的问题
springboot使用的redisTemplate进行jedis操作,与直接用jedis操作略有不同
使用事务只能用SessionCallback
注意逻辑,先上乐观锁,再看其他的,判断用户是否以及存在和是否有货不需要事务
先上乐观锁:判断要处在乐观锁之内,如果乐观锁放在了第一个else里,会出现刚刚进去时显示有货,然后开枪导致出现负数,因为后面不保证是否有货了,而保证的是否那个时刻只有一个人可以抢上
如下图,这一瞬间可能发送多次这个问题
==先上乐观锁好处==:因为乐观锁在所有操作之前,假设只有一个剩余了,一旦有人抢上,其他的的操作都将无效
库存遗留问题
秒杀时人多货少,大概率所有货都会卖出
但是一旦货量比较多,在使用乐观锁锁了后,会导致一瞬间结束后,因为乐观锁导致大量用户没抢上却还有存货,此事可以使用lua脚本配合使用
lua脚本
local userid=KEYS[1];
local prodid=KEYS[2];
local qtkey="commodity:"..prodid;
local usersKey="snapped"..prodid;
local userExists=redis.call("sismember",usersKey,userid);
if tonumber(userExists)==1 then
return 2;
end
local num= redis.call("get" ,qtkey);
if tonumber(num)<=0 then
return 0;
else
redis.call("decr",qtkey);
redis.call("sadd",usersKey,userid);
end
return 1;
java代码 使用spring-boot-data-redis-starter
先添加一个DefaultRedisScript
@Bean
public DefaultRedisScript<Long> redisScript() {
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptSource(new StaticScriptSource(script));
redisScript.setResultType(Long.class);
return redisScript;
}
使用
==注意要使用long而不是integer,lua中的int对应java中的long==
Long execute = redisTemplate.execute(defaultRedisScript,List.of(uid.toString(),"1101"));
Last updated