Redis系列:五大基本类型和三大特殊类型详细解析(二)

本文详细介绍了 Redis 的五大基本数据类型(字符串、列表、集合、哈希)及其使用场景,并探讨了三大特殊类型(有序集合、基数统计、位图)的应用,例如计数器、队列、地理位置信息存储等。通过实例展示了各种操作命令,如设置、获取、更新、删除等,以及如何利用这些数据类型解决实际问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

序言

Redis是一个开源(BSD许可),内存中的数据结构存储,用作数据库、缓存和消息代理。它支持数据结构,如字符串、哈希、列表、集合、带范围查询的排序集、位图、超日志、带有radius查询和流的地理空间索引。这篇文章我们就来详细的解析这五大数据类型和三大特殊类型,结合现实生活的一些应用,希望有所帮助。

Redis-key的基本知识

127.0.0.1:6379> set name xiaotang //设置key
OK
127.0.0.1:6379> keys *   //查看所有key
1) "name"
127.0.0.1:6379> exists name  //判断key是否存在
(integer) 1
127.0.0.1:6379> move name 1  //移出当前key到数据库1
(integer) 1
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> set name xiaowang
OK
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379> expire name 10  //给key设置过期时间,单位是秒
(integer) 1
127.0.0.1:6379> ttl name  //查看key过期的剩余时间
(integer) 6
127.0.0.1:6379> ttl name
(integer) 5
127.0.0.1:6379> ttl name
(integer) 3
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> set name xiaoxiao
OK
127.0.0.1:6379> type name  //查看key的类型
string

不会的命令可以到官网查看redis.

五大基本数据类型

String(字符串)基本类型

127.0.0.1:6379> set key1 v1  //设置值
OK
127.0.0.1:6379> get key1   //获得key1的值
"v1"
127.0.0.1:6379> append key1 "hellow"  //追加字符串,如果当前key不存在就相当于set key,否则在后面追加
(integer) 8
127.0.0.1:6379> get key1
"v1hellow"
127.0.0.1:6379> strlen key1 //获取字符串的长度
(integer) 8
127.0.0.1:6379> 

比如平时我们看到的微信里一些浏览量,在java中简单的i++i+=,我们也可以用redis实现:

127.0.0.1:6379> set views 0  //设置浏览量0
OK
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> incr views  //自增1
(integer) 1
127.0.0.1:6379> get views
"1"
127.0.0.1:6379> incr views 
(integer) 2
127.0.0.1:6379> get views
"2"
127.0.0.1:6379> decr views  //自减1
(integer) 1
127.0.0.1:6379> get views
"1"
127.0.0.1:6379> incrby views 10 //可以设置步长,指定增量,这里为10 ,自增
(integer) 11
127.0.0.1:6379> get views
"11"
127.0.0.1:6379> decrby views 10 //可以设置步长,指定增量,这里为10 ,自减
(integer) 1
127.0.0.1:6379> get views
"1"
127.0.0.1:6379> 

字符串的范围(range):

127.0.0.1:6379> set key1 "hellow,xiaotang"
OK
127.0.0.1:6379> get key1
"hellow,xiaotang"
127.0.0.1:6379> getrange key1 0 3  //截取字符串
"hell"
127.0.0.1:6379> getrange key1 0 -1 //获得全部字符串,相当于get
"hellow,xiaotang"
127.0.0.1:6379> set key2 abcdef
OK
127.0.0.1:6379> get key2
"abcdef" 
127.0.0.1:6379> setrange key2 1 xx //替换指定位置的字符串
(integer) 6
127.0.0.1:6379> get key1
"hellow,xiaotang"
127.0.0.1:6379> get key2
"axxdef"
127.0.0.1:6379> 

setex(set with expire)设置过期时间
setnx (set if not exist)不存在的设置(在分布式锁中经常会用)

127.0.0.1:6379> setex key3 30 "hellow" //设置key3的值为hellow,30秒后过期
OK
127.0.0.1:6379> ttl keys
(integer) -2
127.0.0.1:6379> ttl key3
(integer) 20
127.0.0.1:6379> setnx key4 "redis"  //如果key4不存在,创建key4
(integer) 1
127.0.0.1:6379> get key4
"redis"
127.0.0.1:6379> setnx key4 "hahah"  //如果key4存在,创建失败
(integer) 0
127.0.0.1:6379> get key4
"redis"

mset 批量设置
mget 批量获取

127.0.0.1:6379> mset k1 v1 k2 v2 //批量设置
OK
127.0.0.1:6379> keys *
1) "k2"
2) "k1"
127.0.0.1:6379> mget k1 k2 //批量获取
1) "v1"
2) "v2"
//如果存在添加失败,不存在添加成功,k4不存在,理论上能添加成功,可是从结果可以看出没有添加成功
//可以看出msetnx 是一个原子操作,要么一起成功,要么一起失败
127.0.0.1:6379> msetnx k1 v1 k4 v4  
(integer) 0
127.0.0.1:6379> keys *
1) "k2"
2) "k1"

set user:1{name:xiaotang,age:20} 设置uer:1对象,值为json字符串保存一个对象,一般情况是这样!!
在这里我们可以用mset直接设置:

127.0.0.1:6379> mset user:1:name xiaotang user:1:age 20
OK
127.0.0.1:6379> keys *
1) "user:1:age"
2) "user:1:name"
127.0.0.1:6379> mget user:1:name user:1:age
1) "xiaotang"
2) "20"

getset 先get再set:

127.0.0.1:6379> getset db redis //如果不存在返回null,再设置值
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mysql //如果存在先返回存在的值,再设置值
"redis"
127.0.0.1:6379> get db
"mysql"

String类型的使用场景:(1)计数器(2)统计各个单位数量(3)对象缓存的存储等等

List(列表)

在redis中,我们可以把list玩成栈,队列,阻塞队列
向list中插值,查看list中的值:

//将一个值或者多个值从左边插入(相当于栈,先进后出)
127.0.0.1:6379> lpush list one 
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> lrange list 0 -1 //获取list中的值
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 1
1) "three"
2) "two"

//将一个值或者多个值从右边插入(相当于队列,先进先出)
127.0.0.1:6379> rpush list2 one
(integer) 1
127.0.0.1:6379> rpush list2 two
(integer) 2
127.0.0.1:6379> rpush list2 three
(integer) 3
127.0.0.1:6379> lrange list2 0 -1
1) "one"
2) "two"
3) "three"

移出一个元素,lpop 从左边移除 rpop从右边移除:

127.0.0.1:6379> lrange list2 0 -1
1) "one"
2) "two"
3) "three"
127.0.0.1:6379> lpop list2
"one"
127.0.0.1:6379> lrange list2 0 -1
1) "two"
2) "three"
127.0.0.1:6379> rpop list2
"three"
127.0.0.1:6379> lrange list2 0 -1
1) "two"

lindex 根据下标获取值:

127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lindex list 1
"two"

llen 获取列表的长度:

127.0.0.1:6379> llen list
(integer) 3

移除指定的值,lrem

127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lpush list three
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379> lrem list 1 one //移除一个one
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "three"
3) "two"
127.0.0.1:6379> lrem list 2 three //移除2个three
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "two"

ltrim 通过下表截取指定的长度:

127.0.0.1:6379> lpush list a
(integer) 1
127.0.0.1:6379> lpush list b
(integer) 2
127.0.0.1:6379> lpush list c
(integer) 3
127.0.0.1:6379> lpush list d
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "d"
2) "c"
3) "b"
4) "a"
//截取下标为1和2的值
127.0.0.1:6379> ltrim list 1 2
OK
127.0.0.1:6379> lrange list 0 -1
1) "c"
2) "b"

rpoplpush 移除一个list的最后一个元素,添加到另外一个list中:

127.0.0.1:6379> rpush list1 list1_one
(integer) 1
127.0.0.1:6379> rpush list1 list1_two
(integer) 2
127.0.0.1:6379> rpush list1 list1_three
(integer) 3
127.0.0.1:6379> lrange list1 0 -1
1) "list1_one"
2) "list1_two"
3) "list1_three"
127.0.0.1:6379> rpush list2 list2_a
(integer) 1
127.0.0.1:6379> rpush list2 list2_b
(integer) 2
127.0.0.1:6379> rpush list2 list2_c
(integer) 3
127.0.0.1:6379> lrange list2 0 -1
1) "list2_a"
2) "list2_b"
3) "list2_c"
//把list1的最后一个元素移入到list2中
127.0.0.1:6379> rpoplpush list1 list2  
"list1_three"
127.0.0.1:6379> lrange list1 0 -1
1) "list1_one"
2) "list1_two"
127.0.0.1:6379> lrange list2 0 -1
1) "list1_three"
2) "list2_a"
3) "list2_b"
4) "list2_c"

lset 将列表中指定下标的值替换成另外一个值,更新操作:

127.0.0.1:6379> lrange list2 0 -1
1) "list2_a"
2) "list2_b"
127.0.0.1:6379> lset list2 0 a
OK
127.0.0.1:6379> lrange list2 0 -1
1) "a"
2) "list2_b"

小结:list实际上是一个链表,既可以作为队列也可以作为栈

Set(集合)

set中的值不能重复,set开头都是s
sadd添加和smembers获取和sismember判断元素是否存在:

127.0.0.1:6379> sadd list one  //set中添加元素
(integer) 1
127.0.0.1:6379> sadd list two
(integer) 1
127.0.0.1:6379> sadd list three
(integer) 1
127.0.0.1:6379> smembers list  //查看set所有值
1) "one"
2) "two"
3) "three"
127.0.0.1:6379> sismember list one  //判断某个值是否在set中
(integer) 1
127.0.0.1:6379> sismember list for
(integer) 0

scard 获取set元素的个数:

127.0.0.1:6379> scard list
(integer) 3

srem 移除某一个元素:

127.0.0.1:6379> smembers set
1) "one"
2) "two"
127.0.0.1:6379> srem set one
(integer) 1
127.0.0.1:6379> smembers set
1) "two"

srandmember 随机选出一个元素:

127.0.0.1:6379> sadd set one
(integer) 1
127.0.0.1:6379> srandmember set
"one"
127.0.0.1:6379> srandmember set
"two"
127.0.0.1:6379> srandmember set 2
1) "one"
2) "two"

spop 随机删除一些set集合中的元素:

127.0.0.1:6379> spop set
"two"
127.0.0.1:6379> smembers set
1) "one"

smove 将一个集合中的值移到另外一个集合中:

127.0.0.1:6379> sadd myset1 one
(integer) 1
127.0.0.1:6379> sadd myset1 two
(integer) 1
127.0.0.1:6379> sadd myset2 three
(integer) 1
127.0.0.1:6379> keys *
1) "myset1"
2) "myset2"
127.0.0.1:6379> smembers myset1
1) "one"
2) "two"
127.0.0.1:6379> smembers myset2
1) "three"
127.0.0.1:6379> smove myset1 myset2 one  //将myset1集合中的one 移到 myset2中
(integer) 1
127.0.0.1:6379> smembers myset2
1) "one"
2) "three"
127.0.0.1:6379> smembers myset1
1) "two"

数字集合类,比如我们共同关注热点之类的
差集:sdiff
交集:sinter
并集:sunion

127.0.0.1:6379> sadd key1 a
(integer) 1
127.0.0.1:6379> sadd key1 b
(integer) 1
127.0.0.1:6379> sadd key1 c
(integer) 1
127.0.0.1:6379> sadd key2 c
(integer) 1
127.0.0.1:6379> sadd key2 d
(integer) 1
127.0.0.1:6379> sadd key2 e
(integer) 1
127.0.0.1:6379> smembers key1
1) "b"
2) "a"
3) "c"
127.0.0.1:6379> smembers key2
1) "e"
2) "d"
3) "c"
127.0.0.1:6379> sdiff key1 key2 //差集,在key1中有,在key2中没有
1) "b"
2) "a"
127.0.0.1:6379> sinter key1 key2 //交集
1) "c"
127.0.0.1:6379> sunion key1 key2 //并集
1) "e"
2) "a"
3) "c"
4) "b"
5) "d"

Hash(哈希)

Map集合,key-value,命令以h开头。
设置一个hset ,设置多个hmset,获取一个hget ,获取多个hmget ,获取全部hgetall ,删除hdel ,获取长度hlen
判断hash中指定字段是否存在hexists ,只获取所有key值hkeys ,只获取所有values值hvals

127.0.0.1:6379> hset myhash name xiaotang //设置一个具体的key-value
(integer) 1
127.0.0.1:6379> hget myhash name  //获取一个字段值
"xiaotang"
127.0.0.1:6379> hmset myhash2 name xiaoxiao age 20 //设置多个key-value
OK
127.0.0.1:6379> hmget myhash2 name age //获取多个字段的值
1) "xiaoxiao"
2) "20"
127.0.0.1:6379> hgetall myhash2 //获取全部的数据
1) "name"
2) "xiaoxiao"
3) "age"
4) "20"
127.0.0.1:6379> hdel myhash2 age  //删除hash指定的key-value
(integer) 1
127.0.0.1:6379> hgetall myhash2
1) "name"
2) "xiaoxiao"
127.0.0.1:6379> hlen myhash2  //获取hash长度
(integer) 1
127.0.0.1:6379> hexists myhash2 name //判断hash中指定字段是否存在
(integer) 1
127.0.0.1:6379> hkeys myhash2  //只获取所有key
1) "name"
127.0.0.1:6379> hvals myhash2  //只获取所有values
1) "xiaoxiao"

hash更适合对象的存储,String适合字符串的存储

Zset(有序集合)

在set的基础上增加一个值,以z开头。

127.0.0.1:6379> zadd myset 1 one //添加一个值或多个值,要在每个值前加上编号123....
(integer) 1
127.0.0.1:6379> zadd myset 2 two
(integer) 1
127.0.0.1:6379> zrange myset 0 -1 //查看所有
1) "one"
2) "two"

排序如何实现zrangebyscore,从低到高(默认)zrange ,从高到低zrevrange

127.0.0.1:6379> zadd salary 500 xiaowang
(integer) 1
127.0.0.1:6379> zadd salary 100 xiaoming
(integer) 1
127.0.0.1:6379> zadd salary 1000 xiaotang
(integer) 1
127.0.0.1:6379> keys *
1) "salary"
127.0.0.1:6379> zrange salary 0 -1 withscores //获取全部数据
1) "xiaoming"
2) "100"
3) "xiaowang"
4) "500"
5) "xiaotang"
6) "1000"
127.0.0.1:6379> zrangebyscore salary 500 +inf withscores //显示500到正无穷工资的员工
1) "xiaowang"
2) "500"
3) "xiaotang"
4) "1000"
127.0.0.1:6379> zrange salary 0 -1 withscores //默认从低到高
1) "xiaocheng"
2) "100"
3) "xiaowang"
4) "500"
5) "xiaotang"
6) "1000"
127.0.0.1:6379> zrevrange salary 0 -1 withscores //从高到低排序
1) "xiaotang"
2) "1000"
3) "xiaowang"
4) "500"
5) "xiaocheng"
6) "100"

移除元素zrem,获取有序集合的个数zcard

127.0.0.1:6379> zrange salary 0 -1 withscores
1) "xiaoming"
2) "100"
3) "xiaowang"
4) "500"
5) "xiaotang"
6) "1000"
127.0.0.1:6379> zrem salary xiaoming  //移除元素xiaoming
(integer) 1
127.0.0.1:6379> zrange salary 0 -1 withscores
1) "xiaowang"
2) "500"
3) "xiaotang"
4) "1000"
127.0.0.1:6379> zcard salary //获取有序集合的个数
(integer) 2

案例思路:成绩表,工资标,消息带权重判断,排行榜

三种特殊数据类型

geospatial(地理空间)

案例:朋友的定位,附近的人,打车距离的计算等等
Redis的geospatial,可以推算地理位置的信息,两地之间的距离及方圆几千米的人等等
城市的经度纬度查询网站地址,通过这个网站查询一些测试数据
在这里插入图片描述
在官网中我们可以看到就6个命令,接下来我们开始测试:
geoadd:将指定的地理位置(经度,纬度,名称)添加到指定的key中,例子:geoadd key value(经度 维度 名称),我们开始添加城市数据:

# 两极无法添加
# 一般情况下我们会下载这些城市的数据,通过java程序一次性导入
# `geoadd  key  value(经度 纬度  名称)`
127.0.0.1:6379> geoadd china:city 118.78 32.04  nanjing
(integer) 1
127.0.0.1:6379> geoadd china:city 121.48 31.22 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 119.95 31.79 changzhou
(integer) 1
127.0.0.1:6379> geoadd china:city 106.54 29.59 chongqing
(integer) 1
127.0.0.1:6379> geoadd china:city 116.46 39.32 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 120.19 30.26 hangzhou
(integer) 1
127.0.0.1:6379> geoadd china:city 108.95 34.27 xian
(integer) 1

geopos:获取指定城市的经度和维度:

127.0.0.1:6379> geopos china:city beijing
1) 1) "116.45999997854232788"
   2) "39.32000012654866339"
127.0.0.1:6379> geopos china:city beijing chongqing
1) 1) "116.45999997854232788"
   2) "39.32000012654866339"
2) 1) "106.54000014066696167"
   2) "29.58999896356930748"

geodist:返回两个定位置之间的距离
单位:m 表示单位米 km 表示单位千米 mi 表示单位英里 ft 表示单位英尺

# 常州到重庆的直线距离1304公里
127.0.0.1:6379> geodist china:city changzhou chongqing km
"1304.8621"
127.0.0.1:6379> geodist china:city changzhou shanghai km
"158.3359"

georadius:以给定的经纬度为中心,找出某一半径内的所有元素
案例分析:微信中附近的人,我们每个人打开手机的定位,都会有一个位置信息坐标(实时变化的),我们把他们放到一个集合中,然后根据自己的位置,以多少米为半径到这个定位位置的集合中查询,及就可以知道你的周围有多少人了

# 以100 30 为中心,半径为1000km,在chaina:city集合中查找元素,可以看到有两个
127.0.0.1:6379> georadius china:city 100 30 1000 km
1) "chongqing"
2) "xian"
# withdist 显示之间的距离
127.0.0.1:6379> georadius china:city 100 30 1000 km withdist
1) 1) "chongqing"
   2) "632.8194"
2) 1) "xian"
   2) "966.9684"
# withcoord 显示其它人的经纬度(位置信息)
127.0.0.1:6379> georadius china:city 100 30 1000 km withcoord
1) 1) "chongqing"
   2) 1) "106.54000014066696167"
      2) "29.58999896356930748"
2) 1) "xian"
   2) 1) "108.95000249147415161"
      2) "34.26999911916288966"
 # count 限制查询的数量
127.0.0.1:6379> georadius china:city 100 30 1000 km withcoord count 1
1) 1) "chongqing"
   2) 1) "106.54000014066696167"
      2) "29.58999896356930748"

georadiusbymember:找出指定范围内的元素

# 找出集合中以常州为中心,1000km半径范围内的城市
127.0.0.1:6379> georadiusbymember china:city changzhou 1000 km
1) "hangzhou"
2) "changzhou"
3) "shanghai"
4) "nanjing"
5) "beijing"
127.0.0.1:6379> georadiusbymember china:city changzhou 200 km 
1) "hangzhou"
2) "changzhou"
3) "shanghai"
4) "nanjing"

geohash:返回一个或多个位置元素的hash表示,该命令将返回11个字符的geohash字符串

# 将二维的经纬度变成了一维的字符串,越长越精确
127.0.0.1:6379> geohash china:city changzhou shanghai
1) "wttkce0yjt0"
2) "wtw3s77j9j0"

geo的底层的实现原理其实就是Zset,我们可以使用Zset命令来操作gao:

127.0.0.1:6379> zrange china:city 0 -1
1) "chongqing"
2) "xian"
3) "hangzhou"
4) "changzhou"
5) "shanghai"
6) "nanjing"
7) "beijing"
127.0.0.1:6379> zrem china:city xian
(integer) 1
127.0.0.1:6379> zrange china:city 0 -1
1) "chongqing"
2) "hangzhou"
3) "changzhou"
4) "shanghai"
5) "nanjing"
6) "beijing"

通过上面对geospatial的使用介绍,我想看过文章的人,应该对于比如微信的摇一摇怎么去实现也有一定的思路了吧…

HyperLogLogs(基数统计的算法)

案例:网页UV(比如一个人多次访问网站,判断为一个人),传统的方式,set保存用户的id,然后可以统计set中元素的数量,
0.81的错误率,统计UV可以忽略不计

127.0.0.1:6379> pfadd myset1 a b c d e f //创建一组元素
(integer) 1
127.0.0.1:6379> pfcount myset1 //统计(不会统计重复的)
(integer) 6
127.0.0.1:6379> pfadd myset2 d f q w e r
(integer) 1
127.0.0.1:6379> pfcount myset2
(integer) 6
127.0.0.1:6379> pfmerge myset myset1 myset2 //合并(并集)
OK
127.0.0.1:6379> pfcount myset
(integer) 9

Bitmaps(位图)

位存储(0和1):比如统计用户是否活跃,用户是否登录,学生是否打卡等等
Bitmaps也是一种数据结构,通过操作二进制位(0 和 1)进行记录,比如用户365天的打卡情况,365 = 365bit = 46个字节,非常省内存。
测试:比如我们日常的打卡从11月1日到11月5日,5天从0到4的位置,0代表11月1日:
在这里插入图片描述

127.0.0.1:6379> setbit sign 0 1  //11月1日签到
(integer) 0
127.0.0.1:6379> setbit sign 1 0  //11月2日没签到
(integer) 0
127.0.0.1:6379> setbit sign 2 1
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> getbit sign 3 //可以查看11月4号 打卡了
(integer) 1

统计打卡的天数bitcount,可以看到从11月1日到11月5日打卡了4天:

127.0.0.1:6379> bitcount sign 0 4
(integer) 4

结尾

到这里五大基本类型和三大特殊类型的详细讲解就到这啦,后续更精彩…
肝肝肝,一键三连…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值