Redis是什么
Redis其实是一种存取在内存中,可以持久化在硬盘中的非关系型数据库,才用KV方式存储数据。
nosql的一大代表就是redis,除此之外还有MongoDB、MemcacheDB等
Redis特性
- 内存中都是cpu操作,存取速度快
- 功能丰富,有持久化策略、过期策略等,比较完善
- 高可用,可搭建集群
- 数据类型丰富
- 可支持多种语言
Redis的搭建
一般来讲,我们都是在linux上面搭建redis服务端,window虽然有,但只能用作学习测试
我这边购买的服务器是阿里云轻量级应用服务器,centos7.3
前期准备工作
买好的服务器一般是一台空服务器,配好yum源,装好docker以后,就可以装redis了。
Centos7.3-yum源的更新.
centos7.3-基础环境安装.
centos7.3-数据服务安装.
客户端简单操作
[root@dasdasd /]# docker exec -it redis5 redis-cli
127.0.0.1:6379> select 6 #总共16个库,选择6号库
OK
127.0.0.1:6379[6]> set luhui 111 #set一个值
OK
127.0.0.1:6379[6]> get luhui
"111"
127.0.0.1:6379[6]> keys * #查看所有键
1) "luhui"
127.0.0.1:6379[6]> dbsize #查看键的个数
(integer) 1
127.0.0.1:6379[6]> exists luhui 查看某个键是否存在
(integer) 1
127.0.0.1:6379[6]> exists sun
(integer) 0
127.0.0.1:6379[6]> set sun 423
OK
127.0.0.1:6379[6]> del sun #删除键
(integer) 1
127.0.0.1:6379[6]> type luhui #查看键的类型
string
可视化界面操作
好了 有了以上步骤,后面我们就可以实际去操作学习了
Redis数据类型
分类
String、Hash、List、Set、ZSet、Hyperloglog、Geo、Streams
存储结构
redis是一个KV存储的数据库,kv是通过hashtable实现的,也就是下面的dictEntry
至于value,不管是string还是hash还是set、zset、list,本质都是一个redisObject对象
当然,下面的实现都是C语言
String
string是最基本最常见的,上面的例子就是操作string的,用来存储字符串、数字等
操作命令
set和get
127.0.0.1:6379[6]> set zhangsan 1
OK
127.0.0.1:6379[6]> get zhangsan
"1"
mset和mget
批量设置和批量取值,也是原子性操作
127.0.0.1:6379[6]> mset lisi 2 wangwu 3
OK
127.0.0.1:6379[6]> mget lisi wangwu
1) "2"
2) "3"
setnx和del
这两个组合可以实现分布式锁
- setnx机制:如果键存在,则set失败,如果不存在set成功,可以用来当锁
- del:删除键,也就是释放锁
127.0.0.1:6379[6]> setnx lisi 4
(integer) 0
127.0.0.1:6379[6]> setnx zhaoliu 4
(integer) 1
127.0.0.1:6379[6]> del zhaoliu
(integer) 1
expire key time 可以用这个命令来设置key的失效时间,也就是定时删除,避免del key失败,导致锁无法释放
setnx key value是原子性的,expire key time也是原子性的,但是他们的组合不是原子性的,所以会导致万一setnx执行成功,但是expire执行失败,锁还是无法释放。
set key value time EX = setnx+expire
setnx执行的时候,是无法同时设置失效时间的,但是set可以,
set key value [EX seconds] [PX milliseconds] [NX|XX]
- EX seconds:设置失效时长,单位秒
- PX milliseconds:设置失效时长,单位毫秒
- NX:key不存在时设置value,成功返回OK,失败返回(nil)
- XX:key存在时设置value,成功返回OK,失败返回(nil)
setnx = set key value nx
127.0.0.1:6379[6]> set lock 5 ex 20 nx
OK
# 20s之内设置,设置失败
127.0.0.1:6379[6]> set lock 10 nx
(nil)
# 20s之后设置,设置成功
127.0.0.1:6379[6]> set lock 10 nx
OK
incr/incrby,decr/decrby
自增自减,批量自增自减,也是原子性操作
127.0.0.1:6379[6]> get id
"1"
127.0.0.1:6379[6]> incr id
(integer) 2
127.0.0.1:6379[6]> decr id
(integer) 1
127.0.0.1:6379[6]> incrby id 10
(integer) 11
127.0.0.1:6379[6]> decrby id 10
(integer) 1
对于浮点数,使用incrbyfloat
其他命令
127.0.0.1:6379[6]> set name luhui
OK
# 查看长度
127.0.0.1:6379[6]> strlen name
(integer) 5
# 拼接字符
127.0.0.1:6379[6]> append name cool
(integer) 9
127.0.0.1:6379[6]> get name
"luhuicool"
# 获取指定范围的字符
127.0.0.1:6379[6]> getrange name 0 4
"luhui"
应用场景
缓存
可以存储一些热点数据,提高访问速度
分布式共享
因为redis可以独立部署,所以可以存储共享一些数据,比如session
分布式锁
上面的setnx实现的分布式锁
全局id
自增id incrby id 1
计数器
同样可以使用incrby
限流
同样还是incrby,用访问者的ip为key,访问一次加一次,超过一定次数返回false来拒绝访问
hash
当我们的value不是一个简单的字符串或者数字的时候,是一个对象的话,除了序列化这种开销比较大的方式,还有种方法就是我们的hash
也就是value,类型为上面的redisObject变成hash,不再是string
实现方式
redis本身的实现是通过hashtable实现的,这是外层的哈希。如果value的类型变成hash,那么存储value的方式叫做内层的哈希。
这个内层的哈希有两种实现方式,ziplist和hashtable,当对象较小的时候,用ziplist,较大的时候会自动进化成hashtable
这时候value对应的这个对象,它的value只能是string,不能再往下嵌套了。
操作命令
很多操作其实就是再原来string的命令前加一个h
设置一个值
127.0.0.1:6379[6]> hset luhui name "luhui" age 24
(integer) 2
获取这个值
要想到,现在value不是一个string了,它是一个复杂对象,所以取值方式也肯定会有变化
127.0.0.1:6379[6]> hgetall luhui
1) "name"
2) "luhui"
3) "age"
4) "24"
127.0.0.1:6379[6]> hkeys luhui
1) "name"
2) "age"
127.0.0.1:6379[6]> hvals luhui
1) "luhui"
2) "24"
修改一个字段
127.0.0.1:6379[6]> hset luhui age 25
(integer) 0
127.0.0.1:6379[6]> hgetall luhui
1) "name"
2) "luhui"
3) "age"
4) "25"
修改的时候,如果使用类似setnx的操作,如果字段存在则不做改变,返回0,如果不存在则增加
127.0.0.1:6379[6]> hsetnx luhui name "laolu"
(integer) 0
127.0.0.1:6379[6]> hgetall luhui
1) "name"
2) "luhui"
3) "age"
4) "24"
127.0.0.1:6379[6]> hsetnx luhui weight 67
(integer) 1
127.0.0.1:6379[6]> hgetall luhui
1) "name"
2) "luhui"
3) "age"
4) "24"
5) "weight"
6) "67"
增加一个字段
127.0.0.1:6379[6]> hset luhui height 178
(integer) 1
127.0.0.1:6379[6]> hgetall luhui
1) "name"
2) "luhui"
3) "age"
4) "25"
5) "height"
6) "178"
删除一个字段
127.0.0.1:6379[6]> hdel luhui height
(integer) 1
127.0.0.1:6379[6]> hgetall luhui
1) "name"
2) "luhui"
3) "age"
4) "25"
删除这个key
127.0.0.1:6379[6]> del luhui
(integer) 1
127.0.0.1:6379[6]> hgetall luhui
(empty array)
hlen是获取长度
应用场景
比起string,hash又多了一个应用场景
存储对象
比起string,hash显然更适合存储对象
list
存储结构
存储有序的字符串(从左到右),元素可以重复。可以充当队列和栈的角色
操作命令
设值
lpush从左边入栈,rpush从右边入栈
127.0.0.1:6379[6]> lpush queue a b c
(integer) 3
127.0.0.1:6379[6]> lrange queue 0 -1
1) "c"
2) "b"
3) "a"
127.0.0.1:6379[6]> rpush queue d e f
(integer) 6
127.0.0.1:6379[6]> lrange queue 0 -1
1) "c"
2) "b"
3) "a"
4) "d"
5) "e"
6) "f"
弹出
元素的弹出操作
127.0.0.1:6379[6]> lpop queue
"c"
127.0.0.1:6379[6]> rpop queue
"f"
127.0.0.1:6379[6]> lrange queue 0 -1
1) "b"
2) "a"
3) "d"
4) "e"
取值
lrange key start end
- 第一种使用方式比较好理解,都是正数,start=0,end=3,表示从第0个到第3个元素
- 第二种方式不太好理解,但是使用起来方便,不用担心队列长度,start=0,end=-1,表示从第0个元素到倒数第一个元素。如果是-2,表示从第0个元素到倒数第二元素…
127.0.0.1:6379[6]> lrange queue 0 3
1) "b"
2) "a"
3) "d"
4) "e"
127.0.0.1:6379[6]> lrange queue 0 -1
1) "b"
2) "a"
3) "d"
4) "e"
127.0.0.1:6379[6]> lrange queue 0 -2
1) "b"
2) "a"
3) "d"
拓展
# 查看长度
127.0.0.1:6379[6]> llen queue
(integer) 4
127.0.0.1:6379[6]> lpush queue a a a a
(integer) 8
127.0.0.1:6379[6]> lrange queue 0 -1
1) "a"
2) "a"
3) "a"
4) "a"
5) "b"
6) "a"
7) "d"
8) "e"
# 替换索引为1处的元素
127.0.0.1:6379[6]> lset queue 1 b
OK
127.0.0.1:6379[6]> lrange queue 0 -1
1) "a"
2) "b"
3) "a"
4) "a"
5) "b"
6) "a"
7) "d"
8) "e"
# 在b后面插入c
127.0.0.1:6379[6]> linsert queue after b c
(integer) 9
127.0.0.1:6379[6]> lrange queue 0 -1
1) "a"
2) "b"
3) "c"
4) "a"
5) "a"
6) "b"
7) "a"
8) "d"
9) "e"
# 从左往右遍历,删除1个a
127.0.0.1:6379[6]> lrem queue 1 a
(integer) 1
127.0.0.1:6379[6]> lrange queue 0 -1
1) "b"
2) "c"
3) "a"
4) "a"
5) "b"
6) "a"
7) "d"
8) "e"
# 从右往左遍历,删除1个a
127.0.0.1:6379[6]> lrem queue -1 a
(integer) 1
127.0.0.1:6379[6]> lrange queue 0 -1
1) "b"
2) "c"
3) "a"
4) "a"
5) "b"
6) "d"
7) "e"
# 删除全部a元素
127.0.0.1:6379[6]> lrem queue 0 a
(integer) 2
127.0.0.1:6379[6]> lrange queue 0 -1
1) "b"
2) "c"
3) "b"
4) "d"
5) "e"
# 将链表尾部的元素弹出到头部,循环链表
127.0.0.1:6379[6]> rpoplpush queue queue
"e"
127.0.0.1:6379[6]> lrange queue 0 -1
1) "e"
2) "b"
3) "c"
4) "b"
5) "d"
应用场景
List的特点就是有序,插入删除快捷
用户消息时间线
消息队列
当然,事实上有专门的消息队列,基本不会用redis做消息队列
set
set用的比较少,命令比较少
操作命令
127.0.0.1:6379[6]> sadd myset a b c d e f
(integer) 6
127.0.0.1:6379[6]> smembers myset
1) "b"
2) "a"
3) "d"
4) "f"
5) "e"
6) "c"
# 查看元素个数
127.0.0.1:6379[6]> scard myset
(integer) 6
# 随机获取一个元素
127.0.0.1:6379[6]> srandmember myset
"c"
# 随即弹出一个元素
127.0.0.1:6379[6]> spop myset
"d"
127.0.0.1:6379[6]> smembers myset
1) "f"
2) "a"
3) "e"
4) "c"
5) "b"
# 移除某个元素
127.0.0.1:6379[6]> srem myset a
(integer) 1
127.0.0.1:6379[6]> smembers myset
1) "f"
2) "e"
3) "c"
4) "b"
# 查看元素是否存在
127.0.0.1:6379[6]> sismember myset b
(integer) 1
应用场景
set无序,对于顺序没有要求的可以用这个结构
签到、点赞、打卡等
商品标签
类似这种,某个物品的属性什么的,衣服的大小、尺码啊
zset
有序的set
操作
zset的几个常见的操作如下
127.0.0.1:6379[6]> zadd myzset 10 zhangsan 20 lisi 30 wangwu
(integer) 3
127.0.0.1:6379[6]> zrange myzset 0 -1 withscores
1) "zhangsan"
2) "10"
3) "lisi"
4) "20"
5) "wangwu"
6) "30"
127.0.0.1:6379[6]> zrangebyscore myzset 20 30
1) "lisi"
2) "wangwu"
127.0.0.1:6379[6]> zcard myzset
(integer) 3
127.0.0.1:6379[6]> zcount myzset 20 30
(integer) 2
127.0.0.1:6379[6]> zrank myzset lisi
(integer) 1
应用场景
排行榜等,有序的集合