Redis是一种基于键值对(Key-Value)的NoSQL数据库,它支持string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合)、Bitmaps(位图)、HyperLogLog、GEO(地理信息定位)等多种数据结构和算法。因此Redis可以满足多种应用场景。而且因为Redis会将数据存储在内存中,因此它的读写性能非常好,Redis还具有将数据存到快照或者日志上的机制,这便于数据恢复。Redis还提供键过期、发布订阅、事务、流水线、lua脚本等附加功能
10w/s
RDB
和AOF
两种持久化策略yum install gcc-c++
gcc --version # 校验是否安装成功
在usr
文件夹下输入以下命令下载redis
wget https://download.redis.io/releases/redis-6.2.1.tar.gz
解压redis-6.2.1.tar.gz
tar -zxvf redis-6.2.1.tar.gz
进入到redis-6.2.1
文件夹中,建入make
执行编译,完成后执行make install
,自此安装完成。
注意:请预先安装c语言编译环境,否则到此步骤会报错
redis安装完后会产生几个以redis开头的可执行文件,这些Redis Shell
可以用来启动、停止Redis。
可执行文件 | 作用 |
---|---|
redis-server | 启动Redis |
redis-cli | Redis客户端 |
redis-bechmark | Redis测试工具 |
redis-check-aof | Redis AOF持久化文件检测和修复工具 |
redis-check-rdb | Redis RDB持久化文件检测和修复工具 |
这种方式会使用Redis默认配置来启动,当退出终端或者按住Ctrl+C
就会退出。
redis-server /etc/redis/redis.conf
修改redis.conf,找到daemonize,将此参数设置为yes,如下图所示
启动即可
redis-server /etc/redis/redis.conf
注意最好不要使用kill -9来强制杀死redis服务,这种情况不会做持久化操作,极端情况会造成AOF和复制丢失数据。
该命令会将所有的键输出
127.0.0.1:6379> keys *
(empty array)
# 添加字符串
127.0.0.1:6379> set hello zayton
OK
127.0.0.1:6379> set spring boot
OK
127.0.0.1:6379> set java basic
OK
# 查看所有键
127.0.0.1:6379> keys *
1) "java"
2) "spring"
3) "hello"
# 删除字符串
127.0.0.1:6379> del hello
(integer) 1
# 再次查看所有键
127.0.0.1:6379> keys *
1) "java"
2) "spring"
#插入一个列表类型的键值对
127.0.0.1:6379> rpush mylist a b c d e
(integer) 5
# exists key
127.0.0.1:6379> exists java
(integer) 1
该命令如果键存在则返回1,不存在则返回0
Redis支持键过期时间,当超过过期时间后,会自动删除键,下面我们给键设置10秒过期,然后用ttl命令观察剩余过期时间。
127.0.0.1:6379> set hello zayton
OK
127.0.0.1:6379> expire hello 10
(integer) 1
127.0.0.1:6379> ttl hello
(integer) 8
127.0.0.1:6379> ttl hello
(integer) 6
127.0.0.1:6379> ttl hello
(integer) 4
127.0.0.1:6379> ttl hello
(integer) 4
127.0.0.1:6379> ttl hello
(integer) 1
127.0.0.1:6379> ttl hello
(integer) -2
127.0.0.1:6379>
ttl命令会返回三种返回值:
-1
表示没设置过期时间-2
表示键不存在127.0.0.1:6379> type java
string
127.0.0.1:6379> type mylist
list
set key value [ex seconds] [px milliseconds] [nx|xx]
示例,设置一个key为hello,value为world的字符串
127.0.0.1:6379> set hello world
OK
setnx的作用和set的nx选项一样,都是键不存在才能设置成功。因为redis单线程命令处理机制,如果有多个客户端同时执行setnx key value
,则只会有一个成功,因此setnx常用于分布式锁,是一种乐观锁。上述设置了key为hello的键值对,下面我们用setnx命令再次设置,会发现值没有变化。
127.0.0.1:6379> setnx hello world2
(integer) 0
127.0.0.1:6379> get hello
"world"
setex相当于set指令的ex选项
# 设置一个10s过期的键值对
127.0.0.1:6379> SETEX zayton 10 squid
OK
127.0.0.1:6379> get zayton
"squid"
127.0.0.1:6379> get zayton
"squid"
# 10s后查询
127.0.0.1:6379> get zayton
(nil)
get key
mset key value [key value ...]
gset key [key ...]
示例
127.0.0.1:6379> mset a 1 b 2 c 3
OK
127.0.0.1:6379> mget a b c
1) "1"
2) "2"
3) "3"
注意:如果需要从redis中获取大量key值,建议使用mget
执行n次get命令请求模型如下图所示
# 耗时
n次get时间 = n次网络时间 + n次命令时间
#耗时
n次get时间 = 1次网络时间 + n次命令时间
从耗时我们可以看出,一次mget
请求只需要消耗一次网络时间,而n次get
请求需要消耗n次网络时间,故而使用mget
有助于提高业务处理效率。
incr key
命令用于对值进行自增操作,若值存在且为整数,则自增;若值不存在,则创建并设置初始值为1;若值存在且非整数,则返回错误。并且因为redis
是单线程架构,无需考虑使用CAS
机制来保证线程安全,而是在服务端中都按顺序自增,所以性能比较好。
127.0.0.1:6379> incr num
(integer) 1
127.0.0.1:6379> set zayton squid
OK
127.0.0.1:6379> incr zayton
(error) ERR value is not an integer or out of range
127.0.0.1:6379> incr num
(integer) 2
除了incr key
命令,redis还提供了decr key(自减)
、incrby key (自增指定数字)
、
decrby key(自减指定数字)
、incrbyfloat key(自增浮点数)
127.0.0.1:6379> decr num
(integer) 1
127.0.0.1:6379> incrby num 10
(integer) 11
127.0.0.1:6379> decrby num 5
(integer) 6
127.0.0.1:6379> incrbyfloat floatNum 2.2
"2.2"
如下所示,可将常用的数据库数据存到redis中提高访问数据,建议使用的key为表名:对象名:id,例如userInfo:user:1
用于获取用户的基础信息示例
public UserInfo getUserInfo(long id){
userRedisKey = "user:info:" + id;
// 从Redis获取值
value = redis.get(userRedisKey);
if (value != null) {
// 将值进行反序列化为UserInfo并返回结果
userInfo = deserialize(value);
return userInfo;
}
可以用作视频播放量计数,如下代码示例
public long incrVideoCounter(long id) {
key = "video:playCount:" + id;
return redis.incr(key);
}
为了保证用户信息在集群场景下能够共用一个session,可以在另起一台服务器搭建redis用来保存用户信息,避免用户因为负载均衡在各个服务器之间每次都要重新登陆。
在日常中会发现很多验证码登录限制每分钟获取验证码的频率,这是出于安全考虑设计的,那么可以用redis实现该功能。
phoneNum = "138xxxxxxxx";
key = "shortMsg:limit:" + phoneNum;
// SET key value EX 60 NX
isExists = redis.set(key,1,"EX 60","NX");
if(isExists != null || redis.incr(key) <=5){
// 通过
}else{
// 限速
}
哈希类型指存储键值对的数据结构,也叫做字典、关联数组;例如value={{field1,value1},…{filedN,valueN}}。
# 设置值
hset key field value
# 获取值
hget key field
示例
127.0.0.1:6379> hset user:1 name squid
(integer) 1
127.0.0.1:6379> hget user:1 name
"squid"
HDEL key field
示例
127.0.0.1:6379> hdel user:1 name
(integer) 1
hdel可以删除一个或多个field,返回结果为成功删除filed的个数
hlen key
示例
127.0.0.1:6379> hlen user:1
(integer) 1
hmset key field value [field value ...]
hmget key field [field ...]
示例
127.0.0.1:6379> hmset user:1 name zayton age 18 sex male
OK
127.0.0.1:6379> hmget user:1 name age sex
1) "zayton"
2) "18"
3) "male"
hexists key field
示例
127.0.0.1:6379> hexists user:1 name
(integer) 1
# 获取所有field
hkeys key
# 获取所有value
hvals key
# 获取所有field-value
hgetall key
示例
127.0.0.1:6379> hkeys user:1
1) "name"
2) "age"
3) "sex"
127.0.0.1:6379> hvals user:1
1) "zayton"
2) "18"
3) "male"
127.0.0.1:6379> hgetall user:1
1) "name"
2) "zayton"
3) "age"
4) "18"
5) "sex"
6) "male"
建议:若开发过程中果一定要获取全部field-value,可以使用hscan命令,而非hgetall,避免造成redis拥堵。
hincrby key field
hincrbyfloat key field
示例
127.0.0.1:6379> hincrby hash num 1
(integer) 1
127.0.0.1:6379> hincrbyfloat hash money 1.5
"1.5"
hstrlen key field
示例
# 获取user:1 的name的长度
127.0.0.1:6379> HSTRLEN user:1 name
(integer) 6
127.0.0.1:6379>
命令 | 时间复杂度 |
---|---|
hset key field value | O(1) |
hget key field | O(1) |
hdel key field [field …] | O(k),k是field个数 |
hlen key | O(1) |
hgetall key | O(n),n是field总数 |
hmget field [field …] | O(k),k是field个数 |
hmset field value [field value …] | O(k),k是field个数 |
hexists key field | O(1) |
hkeys key | O(n),n是field总数 |
hvals key | O(n),n是field总数 |
hsetnx key field value | O(1) |
hincrby key field increment | O(1) |
hincrbyfloat key field increment | O(1) |
hstrlen key field | O(1) |
在日常开发中,如果我们要缓存某行用户信息,那么使用哈希类型存储非常合适不过了。
使用哈希类型缓存信息主要有以下原因
但是需要注意的是哈希存储相对二维表更加稀疏,如上图,二维表中null的数据,在哈希表中key是完全不存在的,用户使用时需要考虑到这一点。
列表的概念与Java中的List差不多,都是线性结构,可以用来存储多个有序的字符串,允许重复元素,它可以充当队列和栈。
从右边插入元素
rpush key value [value ...]
示例
# 右边一次插入 a b c d e f
127.0.0.1:6379> rpush list a b c d e f
(integer) 6
# lrange key start stop命令可以从左到右获取指定范围内的列表元素
127.0.0.1:6379> lrange list 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
6) "f"
从左边插入元素
lpush key value [value ...]
示例
127.0.0.1:6379> lpush leftList e f g
(integer) 3
127.0.0.1:6379> lrange leftList 0 -1
1) "g"
2) "f"
3) "e"
向某个元素前或者后插入元素
linsert key before|after pivot value
示例:向列表list中的a元素后插入元素s
127.0.0.1:6379> linsert list after a s
(integer) 7
127.0.0.1:6379> lrange list 0 -1
1) "a"
2) "s"
3) "b"
4) "c"
5) "d"
6) "e"
7) "f"
获取指定范围内的元素列表
lrange key start end
示例
# 查询列表list 下标0-2的元素
127.0.0.1:6379> lrange list 0 2
1) "a"
2) "s"
3) "b"
# 查询列表list所有元素
127.0.0.1:6379> lrange list 0 -1
1) "a"
2) "s"
3) "b"
4) "c"
5) "d"
6) "e"
7) "f"
# 查询不存在的列表list2的元素
127.0.0.1:6379> lrange list2 0 1
(empty array)
获取列表指定索引下标的元素
lindex key index
示例
127.0.0.1:6379> lindex list 3
"c"
获取列表长度
llen key
示例
127.0.0.1:6379> llen list
(integer) 7
从列表左侧弹出元素
lpop key
示例
127.0.0.1:6379> lpop list
"a"
127.0.0.1:6379> lrange list 0 -1
1) "s"
2) "b"
3) "c"
4) "d"
5) "e"
6) "f"
从列表右侧弹出
rpop key
示例
127.0.0.1:6379> rpop list
"f"
127.0.0.1:6379> lrange list 0 -1
1) "s"
2) "b"
3) "c"
4) "d"
5) "e"
删除指定元素
lrem key count value
分为以下三种情况:
127.0.0.1:6379> lrange list 0 -1
1) "s"
2) "b"
3) "c"
4) "d"
5) "e"
127.0.0.1:6379> lrem list 1 s
(integer) 1
127.0.0.1:6379> lrem list -1 e
(integer) 1
127.0.0.1:6379> lrem list 0 d
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "b"
2) "c"
按照索引范围修剪列表
ltrim key start end
示例:保留列表list第2个到第5个元素
127.0.0.1:6379> lpush list a a b d
(integer) 6
127.0.0.1:6379> lrange list 0 -1
1) "d"
2) "b"
3) "a"
4) "a"
5) "b"
6) "c"
127.0.0.1:6379> ltrim list 1 4
OK
127.0.0.1:6379> lrange list 0 -1
1) "b"
2) "a"
3) "a"
4) "b"
修改指定索引下标的元素
lset key index newValue
示例:在列表list下标为4的位置插入元素‘zayton’
127.0.0.1:6379> lset list 3 zayton
OK
127.0.0.1:6379> lrange list 0 -1
1) "b"
2) "a"
3) "a"
4) "zayton"
blpop key [key ...] timeout
brpop key [key ...] timeout
blpop示例,若列表为空,当timeout大于0时,时间耗尽后返回nil;当tinmeout等于0时,一直阻塞等待,直到列表有数据。若列表不为空,当timeout大于0时,时间耗尽后返回数据;当timeout等于0时,立即返回数据。
127.0.0.1:6379> brpop blist 3
(nil)
(3.02s)
127.0.0.1:6379> bprop list 0
# 此时通过另一个客户端push元素zayton,该阻塞会弹出元素zayton
127.0.0.1:6379> brpop blist 0
1) "blist"
2) "zayton"
(61.12s)
命令 | 时间复杂度 |
---|---|
rpush key value [value…] | O(k),k是field个数 |
lpush key value [value …] | O(k),k是field个数 |
linsert key beforelafter pivot value | O(n),n是pivot 距离列表头或尾的距离 |
lrange key start end | o(s+n),s是start偏移量,n是start到end的范围 |
lindex key index | O(n),n是索引的偏移量 |
llen key | O(1) |
lpop key | O(1) |
rpop key | O(1) |
lrem count value | O(n),n是field总数 |
ltrim key start end | O(n),n是要裁剪的元素总数 |
lset key index value | O(n),n是索引的偏移量 |
blpop brpop | O(1) |
Redis的lpush+brpop命令组合即可实现阻塞队列,如下图,每个消费者只需要关注自己需要的文章列表即可,生产者只需不断使用;push
添加文章即可。
关于列表更多的使用口诀如下:
集合类型可以保存多个字符串元素,但它的元素是无序的且不能重复,一个集合最多可以存储2^32-1个元素。接下来我们来说说它常见的命令:
sadd key element [element ...]
示例,可以看到第二次添加的元素并未成功
127.0.0.1:6379> sadd set a b c
(integer) 3
127.0.0.1:6379> sadd set a b
(integer) 0
srem key element [element ...]
示例
127.0.0.1:6379> srem set a b
(integer) 2
127.0.0.1:6379> srem set zayton
(integer) 0
scard的时间复杂度为O(1),它不会遍历集合所有元素,而是通过Redis内部的变量得来的。
scard key
示例
127.0.0.1:6379> scard set
(integer) 1
sismember key element
示例
# 不存在则返回0
127.0.0.1:6379> sismember set a
(integer) 0
# 存在则返回1
127.0.0.1:6379> sismember set c
(integer) 1
srandmember key [count]
示例
127.0.0.1:6379> sadd set a b d e
(integer) 4
127.0.0.1:6379> srandmember set 3
1) "e"
2) "a"
3) "b"
# count不填默认为1
127.0.0.1:6379> srandmember set
"a"
与srandmember
相似,只不过spop
会将元素从集合中删除
spop key
示例
127.0.0.1:6379> spop set
"c"
127.0.0.1:6379> smembers set
1) "e"
2) "a"
3) "d"
4) "b"
# Redis从3.2版本开始,spop也支持[count]参数
127.0.0.1:6379> spop set 2
1) "e"
2) "d"
127.0.0.1:6379> smembers set
1) "a"
2) "b"
smembers key
示例
127.0.0.1:6379> smembers set
1) "a"
2) "b"
sinter key [key ...]
示例
127.0.0.1:6379> sadd set1 a b c d
(integer) 4
127.0.0.1:6379> sadd set2 a b e f
(integer) 4
127.0.0.1:6379> sinter set1 set2
1) "b"
2) "a"
sunion key [key ...]
示例
127.0.0.1:6379> sunion set1 set2
1) "e"
2) "f"
3) "a"
4) "d"
5) "c"
6) "b"
这个差集指的是首个集合中有的而其他集合中没有的元素。
sdiff key [key ...]
示例
127.0.0.1:6379> sdiff set1 set2
1) "d"
2) "c"
sinterstore destination key [key ...]
sunionstore destination key [key ...]
sdiffstore destination key [key ...]
示例
127.0.0.1:6379> sinterstore set3 set1 set2
(integer) 2
127.0.0.1:6379> smembers set3
1) "a"
2) "b"
127.0.0.1:6379> sunionstore set4 set1 set2
(integer) 6
127.0.0.1:6379> smembers set4
1) "e"
2) "f"
3) "a"
4) "d"
5) "c"
6) "b"
127.0.0.1:6379> sdiffstore set5 set1 set2
(integer) 2
127.0.0.1:6379> smembers set5
1) "d"
2) "c"
命令 | 时间复杂度 |
---|---|
zadd key score member [score member …] | O(k×1og(n)),h是添加成员的个数,"是当前有序集合成员个数 |
zcard key | O(1) |
scard key | O(1) |
sismember key element | O(1) |
srandmember key [count] | O(count) |
spop key | O(1) |
smembers key | O(n) ,n是元素总数 |
sinter key [key …]或者 sinterstore | O(m*k),k是多个集合中元素最少的个数,m是键个数 |
sunion key [key …]或者 sunionstore | O(k),k是多个集合元素个数和 |
sdiff key [key …]或者 sdiffstore | O(k),k是多个集合元素个数和 |
与集合差不多,相比集合多了score
属性,使得集合有序,且它保留了集合的不可重复元素,但score
是可重复的。
zadd key score member [score member ...]
示例
127.0.0.1:6379> zadd user:ranking 1 zayton 2 jack 3 james
(integer) 3
redis3.2添加了四个选项:
zcard key
示例
127.0.0.1:6379> zcard user:ranking
(integer) 3
zscore key member
示例
127.0.0.1:6379> zscore user:ranking zayton
"1"
# 如果成员不存在则返回nil
127.0.0.1:6379> zscore user:ranking squid
(nil)
# 升序排名
zrank key member
# 降序排名
zrevrank key member
示例
127.0.0.1:6379> zrank user:ranking zayton
(integer) 0
127.0.0.1:6379> zrevrank user:ranking zayton
(integer) 2
zrem key member [member ...]
示例
127.0.0.1:6379> zrem user:ranking zayton
(integer) 1
zincrby key increment member
示例
127.0.0.1:6379> zincrby user:ranking 20 jack
"22"
127.0.0.1:6379> zincrby user:ranking 4 jack
"26"
zrange key start end [withscores]
zrevrange key start end [withscores]
示例
127.0.0.1:6379> zrange user:ranking 0 -1 withscores
1) "james"
2) "3"
3) "jack"
4) "26"
zrangebyscore表示升序,zrevrangebyscore表示降序,min和max还支持开区间(小括号)和闭区间(中括号),-inf和+inf分别代表无限小和无限大
zrangebyscore key min max [withscores] [limit offset count]
zrevrangebyscore key max min [withscores] [limit offset count]
示例
127.0.0.1:6379> zrangebyscore user:ranking 0 50 withscores
1) "james"
2) "3"
3) "jack"
4) "26"
# 获取20到无限大的成员
127.0.0.1:6379> zrangebyscore user:ranking (20 +inf withscores
1) "jack"
2) "26"
zcount key min max
示例
127.0.0.1:6379> zcount user:ranking 10 30
(integer) 1
zremrangebyrank key start end
示例
127.0.0.1:6379> zadd user:ranking 40 zayton
(integer) 1
127.0.0.1:6379> zrange user:ranking 0 -1
1) "james"
2) "jack"
3) "zayton"
127.0.0.1:6379> zremrangebyrank user:ranking 0 2
(integer) 3
zremrangebyscore key min max
示例
127.0.0.1:6379> zadd user:ranking 10 zayton 24 jack 26 james 35 squid
(integer) 4
127.0.0.1:6379> zremrangebyscore user:ranking 0 25
(integer) 2
zinterstore destination numkeys key [key ...] [weights weight [weight ...]] [aggregate sum|min|max]
这个命令参数较多,下面分别进行说明:
127.0.0.1:6379> zinterstore zset3 2 zset1 zset2
(integer) 2
127.0.0.1:6379> zrange zset3 0 -1 withscores
1) "jack"
2) "73"
3) "squid"
4) "135"
# zset2的权重变为0.5,并且取两个集合中最大值
127.0.0.1:6379> zinterstore zset3 2 zset1 zset2 weights 1 0.5 aggregate max
(integer) 2
127.0.0.1:6379> zrange zset3 0 -1 withscores
1) "jack"
2) "24.5"
3) "squid"
4) "50"
zunionstore destination numkeys key [key ...] [weights weight [weight ...]] [aggregate sum|min|max]
示例
127.0.0.1:6379> zunionstore zset4 2 zset1 zset2
(integer) 7
127.0.0.1:6379> zrange zset4 0 -1 withscores
1) "zayton"
2) "10"
3) "tom"
4) "19"
5) "james"
6) "26"
7) "jack"
8) "73"
9) "jerry"
10) "77"
11) "mike"
12) "101"
13) "squid"
14) "135"
命令 | 时间复杂度 |
---|---|
zadd key score member [score member …] | O(k*log(n)),是添加成员的个数,n是当前有序集合成员个数 |
zcard key | O(1) |
zscore key member | O(1) |
zrank key member zrevrank key member |
O(log(n)),n 是当前有序集合成员个数 |
zrem key member [member …] | O(k*log(n)),是删除成员的个数,n是当前有序集合成员个数 |
zincrby key increment member | O(log(n)),n 是当前有序集合成员个数 |
zrange key start end [withscores] zrevrange key start end [withscores] |
O(log(n)+k),k是要获取的成员个数,n是当前有序集合成员个数 |
zrangebyscore key max min [withscores] zrevrangebyscore key max min [withscores] |
O(log(n)+k),k是要获取的成员个数,n是当前有序集合成员个数 |
zcount | O(log(n)),n 是当前有序集合成员个数 |
zremrengebyrank key start end | O(log(n)+k),k是要删除的成员个数,n是当前有序集合成员个数 |
zremrangebyscore key min max | O(log(n)+k),k是要删除的成员个数,n是当前有序集合成员个数 |
zinterstore destination numkeys key [key …] | O(nk+O(mlog(m)),n是成员数最小的有序集合成员个数,k是有序集合的个数,m 是结果集中成员个数 |
zunionstore destination numkeys key [key …] | O(nk+O(mlog(m)),n是成员数最小的有序集合成员个数,k是有序集合的个数,m 是结果集中成员个数 |
可用于点赞、播放量排行榜等。
例如点赞功能:
# 张三获得10个点赞数
127.0.0.1:6379> zadd user:ranking:20230901 10 zhangsan
(integer) 1
# 小三获得3个点赞数
127.0.0.1:6379> zadd user:ranking:20230901 3 xiaosan
(integer) 1
# 老三获得6个点赞数
127.0.0.1:6379> zadd user:ranking:20230901 6 laosan
(integer) 1
# 张三点赞数+1
127.0.0.1:6379> zincrby user:ranking:20230901 1 zhangsan
"11"
# 查看点赞数前三
127.0.0.1:6379> zrevrange user:ranking:20230901 0 2
1) "zhangsan"
2) "laosan"
3) "xiaosan"
# 张三被取消一个点赞
127.0.0.1:6379> zincrby user:ranking:20230901 -1 zhangsan
"10"
127.0.0.1:6379> zrevrange user:ranking:20230901 0 2
1) "zhangsan"
2) "laosan"
3) "xiaosan"
# 小三是作弊的,被删掉
127.0.0.1:6379> zrem user:ranking:20230901 xiaosan
(integer) 1
127.0.0.1:6379> zrevrange user:ranking:20230901 0 2
1) "zhangsan"
2) "laosan"
https://book.douban.com/subject/26971561/