Redis学习笔记二:基本命令与使用场景

作者:Grey

原文地址: Redis学习笔记二:基本命令

Redis的安装

参考:
Redis学习笔记一:安装和配置

Redis和Memcached对比

https://www.redis.com.cn/redis-vs-memcached.html

其中有一个比较重要的区别是关于其提供的数据结构区别

Memcached

在其数据结构中仅使用字符串和整数。因此,您保存的所有内容都可以是字符串或整数。它很复杂,因为对于整数,您可以做的唯一数据操作是添加或减去它们。如果需要保存数组或对象,则必须先将它们序列化然后保存。要阅读它们,您需要取消序列化。

Redis

具有更强大的数据结构,它不仅可以处理字符串整数,还可以处理二进制安全字符串,二进制安全字符串列表,二进制安全字符串集和有序集。

关于Redis的数据结构:https://zhuanlan.zhihu.com/p/270592490

基本命令

# 如果k1的值设置过,就不设置,如果k1的值没有设置,才会设置k1的值为hello
# msetnx 同理
set k1 hello nx

# 如果k2没设置,就不能设置k2的值为hello,如果k2的值设置过,才能把他设置过hello
set k2 hello xx

# 设置多个值,其中把k1设置xx,把k2设置为33
mset k1 xx k2 33

# 拼接字符串
APPEND k1 " world"

# 范围查找(从0开始)
GETRANGE k1 0 1

# 范围查找(逆序,最右边编号从-1开始)
# 所以0,-1 就是拿到整个字符串
GETRANGE k1 0 -1

# 把k1字符串的第1个(从第0个位置开始)用xxx开始替换后续的字符
# 如果k1是hello,那么执行完下面的语句,k1会被设置为:hxxxo
SETRANGE k1 1 xxx

# 获取k1字符串的长度
STRLEN k1

# 获取数据类型
TYPE k1

# 查询某个命令的使用,如下命令,就是查询SET命令如何使用
help SET

# k1的值+1
INCR k1

# k1的值+22
INCRBY k1 22

# k1的值-1
DECR k1

# k1的值-22
DECRBY k1 22

# k1的值+0.5
INCRBYFLOAT k1 0.5

# 可以使用--raw选项在终端上强制进行原始输出
127.0.0.1:6379> set k3 中
OK
127.0.0.1:6379> get k3
"\xe4\xb8\xad"
127.0.0.1:6379> exit
[root@node1 utils]# redis-cli --raw
127.0.0.1:6379> get k3
中

# 获取k1的值,然后把k1的值设置为hello
GETSET k1 hello

# 设置k1的值为a,如果要把k1的值变为b,也可以通过setbit操作
# 也就是将 01100001 变成 01100010 (a的ASCII码是97,b的ASCII码是98),也就是将'a'中的offset 6从0变成1,将offset 7 从1变成0  (从0开始算)
set k1 a
setbit k1 6 1
setbit k1 7 0

# 查找字符串里面bit值为1的位置 (从左边开始数)
set k1 a
bitpos k1 1
结果为:1

# bitpos的 start end指的是字节位置
# 查找字符串里面bit值为1从第0个字节开始的位置
set k1 ab
BITPOS k1 1 0
1

# 查找字符串里面bit值为0从第1个字节开始的位置
# 其中9的ASCII码为00111001 a的ascii码为01100001
# 所以a9的ASCII码为01100001 00111001
# BITPOS k1 0 1取的位置就是:01100001 ‘0’0111001 中‘’圈住的位置
set k1 a9
BITPOS k1 0 1
9


# BITPOS找不到则返回-1
# 例如:查找字符串里面bit值为1的位置
set k1 "\x00\x00\x00"
BITPOS k1 1
-1

# bitcount统计的是1的数量, bitcount key [start, end] , 其中的start和end指的是byte位置而非bit位置。
# a的ASCII码为01100001
set k1 a
bitcount k1
3

场景1:统计每个用户的登录天数

假设jack这名用户,分别在第6天,第23天,第134天,和第364天登录了系统。

可以执行如下命令:

setbit jack 6 1
setbit jack 23 1
setbit jack 134 1
setbit jack 364 1

统计jack登录的天数,直接可以通过:

bitcount jack

场景2:电商网站派发礼物

假设某个电商网站做活动,在某天要派送礼物,假设这个网站有2亿用户,请问应该备货多少礼物比较适合

思路:
首先,可以考虑一下统计整个网站的活跃用户有多少,比如我们设置三天,1号,2号,3号,统计三天登录的用户获得一个近似的活跃用户的数量:

# 编号为2的用户登在2019年1月1号录了一次
setbit 20190101   2  1
# 编号为3的用户在2019年1月2号登录了一次
setbit 20190102   3  1
# 编号为7的用户在2019年1月2号登录了一次
setbit 20190102   7  1
# 编号为7的用户在2019年1月3号登录了一次
setbit 20190103   7  1

然后通过:

# 将每一天标识的人数(位置上为1)的数进行与运算
bitop  or   destkey 20190101  20190102  20190103

然后求这个destkey中含有的1的数量,即为比较活跃的用户(派发礼物需要准备的礼物数量)

BITCOUNT  destkey

抢购,秒杀,详情页,点赞,评论

都可以使用Redis的incr 方法,这样就可以规避并发下,对数据库的事务操作,完全由redis内存操作代替

更多命令

# lpush和lpop搭配可以实现栈的功能
# lpush和rpop搭配可以实现队列的功能
# 从左边进
lpush k1 a b c
# 从左边弹出
lpop k1
c

# lrange可以从左到右列出元素
lpush k1 a b c
lrange k1
1) "c"
2) "b"
3) "a"

# lindex 可以定位某个元素(从左边开始,从0开始)
lindex k1 0
"c"

# lset 可以设置某个位置的元素
lset k1 0 xxxxx
lrange k1 0 -1
1) "xxxxx"
2) "b"
3) "a"

#LREM key count value
#根据参数 count 的值,移除列表中与参数 value 相等的元素。
#count 的值可以是以下几种:
#count > 0 : 从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count 。
#count < 0 : 从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的绝对值。
#count = 0 : 移除表中所有与 value 相等的值。

LREM k3 2 a

# LINSERT 
lpush k1 a b c d e f g
linsert k1 after b 6 # 也可以用before
lrange k1 0 -1
1) "g"
2) "f"
3) "e"
4) "d"
5) "c"
6) "b"
7) "6"
8) "a"# lpush和lpop搭配可以实现栈的功能
# lpush和rpop搭配可以实现队列的功能
# 从左边进
lpush k1 a b c
# 从左边弹出
lpop k1
c

# lrange可以从左到右列出元素
lpush k1 a b c
lrange k1
1) "c"
2) "b"
3) "a"

# lindex 可以定位某个元素(从左边开始,从0开始)
lindex k1 0
"c"

# lset 可以设置某个位置的元素
lset k1 0 xxxxx
lrange k1 0 -1
1) "xxxxx"
2) "b"
3) "a"




#当 BLPOP被调用时,如果给定 key 内至少有一个非空列表,那么弹出遇到的第一个非空列表的头元素,并和被弹出元素所属的列表的名字 key 一起,组成结果返回给调用者。
# BLPOP可以实现单播FIFO队列
blpop x y z 0

# 打开另外一个redis-cli
# 然后执行
lpush y xxxsd

# 可以看到blpop x y z 0
# 返回了参数
blpop x y z 0
1) "y"
2) "xxxsd"

# blpop 最后一个参数是超时时间,如果设置为0,则不超时

# trim掉第三号元素之前和第五号元素之后的元素,从左边第0个位置开始算

lpush k1 a b c d e f g
(integer) 15
ltrim k1 1 3
OK
lrange k1 0 -1
1) "f"
2) "e"
3) "d"

# Hash
hset person name zs
(integer) 1
hset person age 18 address GZ
(integer) 2
hmget person name age address
1) "zs"
2) "18"
3) "GZ"

hkeys person
1) "name"
2) "age"
3) "address"

hvals person
1) "zs"
2) "18"
3) "GZ"

hgetall person
1) "name"
2) "zs"
3) "age"
4) "18"
5) "address"
6) "GZ"

hincrbyfloat person age 0.5
"18.5"

hincrbyfloat person age -1
"17.5"

sadd k1 a  b c a
(integer) 3

smembers k1
1) "b"
2) "a"
3) "c"

srem k1 a
(integer) 1
smembers k1
1) "b"
2) "c"

# 交集sinter,类似的,还有并集:sunion,差集:sdiff
# sinerstore k k1 k2 将k1和k2交集后的元素存入k
sadd k1 a b c
SMEMBERS k1
1) "a"
2) "c"
3) "b"
sadd k2 a b d
(integer) 3
SINTER k1 k2
1) "a"
2) "b"
SUNION k1 k2
1) "a"
2) "d"
3) "b"
4) "c"
SDIFF k1 k2
"c"

#SRANDMEMBER 命令接受可选的 count 参数:
#如果 count 为正数,且小于集合基数,那么命令返回一个包含 count 个元素的数组,数组中的元素各不相同。如果 count 大于等于集合基数,那么返回整个集合。
#如果 count 为负数,那么命令返回一个数组,数组中的元素可能会重复出现多次,而数组的长度为 count 的绝对值。
# 可以用来抽奖
SRANDMEMBER k3 -3
1) "b"
2) "b"
3) "c"

#有序集
#sorted_set
#Z开头的命令,ZADD,ZCOUNT

zadd fruit 8 apple 2 banana 3 orange
zrange fruit 0 -1
1) "banana"
2) "orange"
3) "apple"
zrange fruit 0 -1 withscores
1) "banana"
2) "2"
3) "orange"
4) "3"
5) "apple"
6) "8"

zcount fruit 3 8
(integer) 2
zscore fruit apple
"8"

#价格由低到高取出前两位
zarange k1 0 1

#价格由高到低取出前两位
zrevarange k1 0 1

zscore fruit apple
"8"
zrank fruit banana
0

zincrby fruit 2.5 banana
"4.5"

127.0.0.1:6379> zadd k1 2 a 3 b 4 c
(integer) 3
127.0.0.1:6379> zadd k2 3 a 1 b 2 c
(integer) 3
127.0.0.1:6379> ZUNIONSTORE k4 2 k1 k2
(integer) 3
127.0.0.1:6379> zrange k4 0 -1
1) "b"
2) "a"
3) "c"
127.0.0.1:6379> ZUNIONSTORE k4 2 k1 k2 aggregate sum
(integer) 3
127.0.0.1:6379> zrange k4 0 -1
1) "b"
2) "a"
3) "c"
127.0.0.1:6379> ZUNIONSTORE k4 2 k1 k2 aggregate max
(integer) 3
127.0.0.1:6379> zrange k4 0 -1
1) "a"
2) "b"
3) "c"
127.0.0.1:6379> ZUNIONSTORE k4 2 k1 k2 aggregate min
(integer) 3
127.0.0.1:6379> zrange k4 0 -1
1) "b"
2) "a"
3) "c"

通过管道连接Redis发送命令

yum install nc
[root@node01 ~]# nc localhost 6379
keys *
*0
set k1 heelo
+OK


[root@node1 utils]# echo -e "set k2 99\nincr k2\n get k2" | nc localhost 6379
+OK
:100
$3
100

关于大量初始化数据

参考大量初始化数据

发布/订阅功能

127.0.0.1:6379> subscribe xxx
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "xxx"
3) (integer) 1
1) "message"
2) "xxx"
3) "hellod"


127.0.0.1:6379> publish xxx hellod
(integer) 1

如果需要考虑获取实时数据和历史数据
关于实时数据

  • pub/sub功能
    关于历史消息
  • 三天前(sorted_set)
  • 更早以前(来自数据库)

架构如下:

redis_db_cache

Redis的事务

执行顺序
exec_tx_order

示例

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set k1 aa
QUEUED
127.0.0.1:6379(TX)> set k2 ddd
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK

watch用法

127.0.0.1:6379> watch k1
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> get k1
QUEUED
127.0.0.1:6379(TX)> keys *
QUEUED
127.0.0.1:6379(TX)> exec
(nil)

另外一个客户端

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> keys *
QUEUED
127.0.0.1:6379(TX)> set k1 ddsdfasdf
QUEUED
127.0.0.1:6379(TX)> exec
1) (empty array)
2) OK

为什么 Redis 的事务不支持回滚(roll back)

摘自:http://www.redis.cn/topics/transactions.html
如果你有使用关系式数据库的经验, 那么 “Redis 在事务失败时不进行回滚,而是继续执行余下的命令”这种做法可能会让你觉得有点奇怪。

以下是这种做法的优点:

Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。
因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。
有种观点认为 Redis 处理事务的做法会产生 bug , 然而需要注意的是, 在通常情况下, 回滚并不能解决编程错误带来的问题。 举个例子, 如果你本来想通过 INCR 命令将键的值加上 1 , 却不小心加上了 2 , 又或者对错误类型的键执行了 INCR , 回滚是没有办法处理这些情况的。

posted @ 2021-04-29 13:15  Grey Zeng  阅读(111)  评论(0编辑  收藏  举报