本篇博客将结合redis在电商项目中的使用,说明redis的常用命令
redis中的数据结构:
业务场景一:缓存商城首页不同分类栏中的商品信息,以优化商城首页的访问速度。For example:以淘宝网为例
1.1.有好货分类栏中的商品广告信息
1.2.爱逛街分类栏中的商品广告信息
等等(可以去淘宝网自己参考)。
1.3 redis实现的缓存机制:(缓存机制的一般规则:先查缓存,再查数据库,如果缓存中没有,则将查询到数据写入缓存)
1.3.1.选择的数据类型为hash,key固定为CONTENT_LIST,field为分类栏id,value为商品信息(数据类型为字符串,这里利用json工具类将java集合对象转换为json字符串),部分业务逻辑代码示例如下:
1.3.2.根据cid查询缓存,将value(json字符串)转换为java的list对象,部分业务逻辑代码示例如下:
1.3.3.缓存同步,当添加新的内容,将相应的缓存删除,这样再次查询的时候回查询数据库并回写缓存
1.3.4.使用到的redis命令
hset key field value
hget key field
hedl key field
hgetall key
使用示例如下
业务场景二:redis实现的分布式锁
2.1.加锁代码
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) {
String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equals(result)) {
return true;
}
return false;
}
}
可以看到,我们加锁就一行代码:jedis.set(String key, String value, String nxxx, String expx, int time),这个set()方法一共有五个形参:
- 第一个为key,我们使用key来当锁,因为key是唯一的。
- 第二个为value,我们传的是requestId,很多童鞋可能不明白,有key作为锁不就够了吗,为什么还要用到value?原因就是我们在上面讲到可靠性时,分布式锁要满足第四个条件解铃还须系铃人,通过给value赋值为requestId,我们就知道这把锁是哪个请求加的了,在解锁的时候就可以有依据。requestId可以使用UUID.randomUUID().toString()方法生成。
- 第三个为nxxx,这个参数我们填的是NX,意思是SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作;
- 第四个为expx,这个参数我们传的是PX,意思是我们要给这个key加一个过期的设置,具体时间由第五个参数决定。
- 第五个为time,与第四个参数相呼应,代表key的过期时间。
总的来说,执行上面的set()方法就只会导致两种结果:1. 当前没有锁(key不存在),那么就进行加锁操作,并对锁设置个有效期,同时value表示加锁的客户端。2. 已有锁存在,不做任何操作。
心细的童鞋就会发现了,我们的加锁代码满足我们可靠性里描述的三个条件。首先,set()加入了NX参数,可以保证如果已有key存在,则函数不会调用成功,也就是只有一个客户端能持有锁,满足互斥性。其次,由于我们对锁设置了过期时间,即使锁的持有者后续发生崩溃而没有解锁,锁也会因为到了过期时间而自动解锁(即key被删除),不会发生死锁。最后,因为我们将value赋值为requestId,代表加锁的客户端请求标识,那么在客户端在解锁的时候就可以进行校验是否是同一个客户端。由于我们只考虑Redis单机部署的场景,所以容错性我们暂不考虑。
2.2. 解锁逻辑:首先获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁(解锁)。
2.3.主要使用到的redis命令
#即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作;设置成功返回“1”,设置失败返回“0”
setnx key value
expire key seconds
del key
使用示例
业务场景三:使用redis的原子计数器incr生成订单号
3.1.Redis原子计数器incr,具有原子性,可以避免高并发下生成重复订单号的情况,测试如下。
模拟100个用户并发的情况
可以得到正确结果
订单生成类,部分逻辑代码
3.2.使用到的redis命令
incr key
decr ordeid
使用示例
业务场景四:将购物车中的商品缓存到redis中
4.1.redis使用的缓存机制:使用hash数据类型,key为cart前缀+用户ID,field为商品ID,value为商品信息(包含了商品的数量)
4.2.添加购物车的部分业务逻辑代码如下所示:
4.3.使用到的redis命令,同业务场景一。(redis中hash数据类型的操作)
4.4.何时清空购物车,订单创建完成之时。
最后一些常用的redis命令
①查询所有key
keys *
使用示例
②list 是有序列表,这个可以玩出很多花样。
比如可以通过 list 存储一些列表型的数据结构,类似粉丝列表、文章的评论列表之类的东西。比如可以通过 lrange 命令,读取某个闭区间内的元素,可以基于 list 实现分页查询,这个是很棒的一个功能,基于 redis 实现简单的高性能分页,可以做类似微博那种下拉不断分页的东西,性能高,就一页一页走。
# 0开始位置,-1结束位置,结束位置为-1时,表示列表的最后一个位置,即查看所有。
lrange mylist 0 -1
比如可以搞个简单的消息队列,从 list 头怼进去,从 list 尾巴那里弄出来。
lpush mylist 1
lpush mylist 2
lpush mylist 3 4 5
rpop mylist
rpush与lpush的区别
rpush(key, value):在名称为key的list尾添加一个值为value的元素
lpush(key, value):在名称为key的list头添加一个值为value的 元素
使用示例
③set 是无序集合,自动去重。
直接基于 set 将系统里需要去重的数据扔进去,自动就给去重了,如果你需要对一些数据进行快速的全局去重,你当然也可以基于 jvm 内存里的 HashSet 进行去重,但是如果你的某个系统部署在多台机器上呢?得基于 redis 进行全局的 set 去重。
可以基于 set 玩交集、并集、差集的操作,比如交集吧,可以把两个人的粉丝列表整一个交集,看看俩人的共同好友是谁?对吧。
把两个大 V 的粉丝都放在两个 set 中,对两个 set 做交集。
#-------操作一个set-------
# 添加元素
sadd mySet 1
# 查看全部元素
smembers mySet
# 判断是否包含某个值
sismember mySet 3
# 删除某个/些元素
srem mySet 1
srem mySet 2 4
# 查看元素个数
scard mySet
# 随机删除一个元素
spop mySet
#-------操作多个set-------
# 将一个set的元素移动到另外一个set
smove yourSet mySet 2
# 求两set的交集
sinter yourSet mySet
# 求两set的并集
sunion yourSet mySet
# 求在yourSet中而不在mySet中的元素
sdiff yourSet mySet
④sorted set 是排序的 set,去重但可以排序,写进去的时候给一个分数,自动根据分数排序。
zadd board 85 zhangsan
zadd board 72 lisi
zadd board 96 wangwu
zadd board 63 zhaoliu
# 获取排名前三的用户(默认是升序,所以需要 rev 改为降序)
zrevrange board 0 3
# 获取某用户的排名
zrank board zhaoliu
使用示例