Redis-03-数据类型

Redis-03-数据类型

1. 五大数据类型

key

1
2
3
4
5
6
7
set  key value	     设置key value
keys * 查看所有的key
exists key 判断当前的key是否存在
expire key seconds 设置过期时间
get key 获得key的值
ttl key 查看当前key的剩余过期时间
type key 查看当前key的一个类型

1.1 String 类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
append key value    			追加字符(如果当前key不存在,就相当于setkey)
strlen key 获取字符串的长度

incre key 自动加1
increby key step 自动增加步长

decr key 自动减1
decrby key 自动减少步长

getrange key start end 截取start到end的字符串
setrange key offset value 指定位置替换

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


mset k1 v1 k2 v2 k3 v3 批量创建值
mget k1 k2 k3 批量获取值


msetnx k1 v1 k2 v2 k4 v4 不存在才会创建(原子性操作)


# 对象

set user:1{name:zhangsan,age:3} 设置user对象为json字符串

mset user:1:name zhangsan user:1:age 3

########
# getset 不存在值返回null,如果存在值,获取原来的值并set新的值
getset 先get再set
127.0.0.1:6379> getset db redis
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mongodb
"redis"
127.0.0.1:6379> get db
"mongodb"

String类型使用类型: value除了是字符串 还可以是 数字 还可以是对象。

  • 计数器(单线程)
  • 分布式锁(单线程)
    • setnx
      • 数据存在就不操作
      • 数据不存在就设置成功
  • 对象存储

mark

mark

1.2 List 类型

mark

mark

在redis里面,list可以做栈或者队列,阻塞队列!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
### List都是以L开头,放到了头部
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
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 1
1) "three"
2) "two"

### R是放到右边
127.0.0.1:6379> Rpush list right
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"

### 移除元素
127.0.0.1:6379> Lpop list
"three"
127.0.0.1:6379> rpop list
"right"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"

### 获得值
127.0.0.1:6379> Lindex list 1
"one"
127.0.0.1:6379> Lindex list 0
"two"


### 长度
127.0.0.1:6379> llen list
(integer) 2

### 移除指定的值
lrem list count value 移除list集合中指定个数的value

### trim 截断
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> rpush mylist "hello" "hello1" "hello2" "hello3"
(integer) 4
127.0.0.1:6379> ltrim mylist 1 2
OK
127.0.0.1:6379> lrange mylist 0 -1
1) "hello1"
2) "hello2"


### rpoplpush 移除列表最后一个元素并给新的列表
127.0.0.1:6379> rpush mylist "hello" "hello1" "hello2"
(integer) 3
127.0.0.1:6379> rpoplpush mylist myotherlist
"hello2"
127.0.0.1:6379> lrange myotherlist 0 -1
1) "hello2"

### lset 将列表中指定下标的值替换成另外一个值,如果不存在列表更新就会报错
127.0.0.1:6379> lset list 0 item
(error) ERR no such key
127.0.0.1:6379> lpush list value1
(integer) 1
127.0.0.1:6379> lrange list 0 0
1) "value1"
127.0.0.1:6379> lset list 0 item
OK
127.0.0.1:6379> lrange list 0 0
1) "item"


### linsert 将某个具体的value插入到某个值前面或者后面
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> rpush mylist "hello" "world"
(integer) 2
127.0.0.1:6379> linsert mylist before world other
(integer) 3
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "other"
3) "world"
127.0.0.1:6379> linsert mylist after world new
(integer) 4
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "other"
3) "world"
4) "new"

mark

  • 可以用来做消息队列!!!

  • 可以做海量数据时Mysql做不到的(order by功能)

    • 拿出最新的消息

mark

mark

  • 以上场景只适用于10W以下的粉丝
    • 接下来思考一下,超级大V该怎么做呢?

1.3 Set 类型

set中的值不能重复!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
### 存值,取值,查看set所有值,判断set中是否存在某值
### set都是以s开头
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> sadd myset "hello"
(integer) 1
127.0.0.1:6379> sadd myset "hello1" "hello2"
(integer) 2
127.0.0.1:6379> smembers myset
1) "hello2"
2) "hello1"
3) "hello"

127.0.0.1:6379> sismember myset hello
(integer) 1
127.0.0.1:6379> sismember myset world
(integer) 0

### 获取长度
127.0.0.1:6379> scard myset
(integer) 3

### 移除指定的元素
127.0.0.1:6379> srem myset "hello"
(integer) 1
127.0.0.1:6379> smembers myset
1) "hello2"
2) "hello1"

### set 无序不重复集合--》随机功能【个数】
127.0.0.1:6379> srandmember myset
"hello2"
127.0.0.1:6379> srandmember myset
"hello1"
127.0.0.1:6379> srandmember myset 2
1) "hello2"
2) "hello1"

### 移除随机的key
127.0.0.1:6379> spop myset
"hello2"
127.0.0.1:6379> spop myset
"hello1"

### 将一个指定的值移动到另外一个set中
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> sadd myset "hello" "hello2" "hello3"
(integer) 3
127.0.0.1:6379> sadd myset 2 "set2"
(integer) 2
127.0.0.1:6379> smove myset myset2 "hello2"
(integer) 1
127.0.0.1:6379> smembers myset
1) "hello"
2) "hello3"
3) "2"
4) "set2"
127.0.0.1:6379> smembers myset2
1) "hello2"

### 微博:共同关注(并集)
数字集合类:
- 差集
- 交集
- 并集
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> sadd key1 a b c
(integer) 3
127.0.0.1:6379> sadd key2 c d e
(integer) 3
127.0.0.1:6379> sdiff key1 key2
1) "a"
2) "b"
127.0.0.1:6379> sinter key1 key2
1) "c"
127.0.0.1:6379> sunion key1 key2
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
  • 可以做的功能:共同关注,共同爱好(社交平台)

mark

mark

(六度分隔理论):六度分隔(Six Degrees of Separation)理论。简单地说:“你和任何一个陌生人之间所间隔的人不会超五个,也就是说,最多通过六个人你就能够认识任何一个陌生人。

  • 微博微信关注模型

mark

  • 共同关注功能
  • 可能认识的人
  • 我关注的人也关注他

1.4 Hash 类型

mark

  • Map集合,key-map! (本质上和string是一样的,唯一的区别是值(value)变成了key-value形式
  • 这个时候值是一个map集合

这里Redis对使用性能有个非常大的忌讳

  • big key
    • 因为redis是单线程的,所以如果有个大key的话,会在这里进行阻塞。

优化这种业务场景

  • hash取模(对数组分组操作)

hash以h开头的命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
### 存值取值
127.0.0.1:6379> hset myhash field1 zhuuu
(integer) 1
127.0.0.1:6379> hget myhash field1
"zhuuu"

127.0.0.1:6379> hmset myhash field1 hello field2 world
OK
127.0.0.1:6379> hmget myhash field1 field2
1) "hello"
2) "world"
127.0.0.1:6379> hgetall myhash
1) "field1"
2) "hello"
3) "field2"
4) "world"

### 本质上和string是一样的,唯一的区别是值(value)变成了key-value形式

### 删除指定的key字段(对应的value也就消失了)
127.0.0.1:6379> hdel myhash field1
(integer) 1
127.0.0.1:6379> hgetall myhash
1) "field2"
2) "world"

### 获取长度
127.0.0.1:6379> hlen myhash
(integer) 1

### 判断hash指定值是否存在
127.0.0.1:6379> hexists myhash field1
(integer) 0
127.0.0.1:6379> hexists myhash field2
(integer) 1

### 只获得所有的field
127.0.0.1:6379> hkeys myhash
1) "field2"

### 只获得所有的value
127.0.0.1:6379> hvals myhash
1) "world"

### 自增和自减
127.0.0.1:6379> hset myhash field3 5
(integer) 1
127.0.0.1:6379> hincrby myhash field3 1
(integer) 6
127.0.0.1:6379> hincrby myhash field3 -1
(integer) 5

### 不存在则可以创建,如果存在就不能设置
127.0.0.1:6379> hsetnx myhash field4 hello
(integer) 1
127.0.0.1:6379> hsetnx myhash field4 hello
(integer) 0

应用:

  • 电商购物车

mark

  • 变更数据(经常变动的值!)
1
2
3
4
127.0.0.1:6379> hset user:1 name zhuuu
(integer) 1
127.0.0.1:6379> hget user:1 name
"zhuuu"
  • hash更适合对象的存储,string更适合字符串的存储

hash缺点

  • 过期的功能不能用在field上面,只能用在key上面
  • Redis集群架构下不太适合大规模的使用

mark

这种集群存在的问题:

  • 数据倾斜的问题:数据都在一个节点中存在的过多
  • 解决方案:对数组进行分组,改变数据存储的位置

1.5 Zset 类型

  • Zset在set的基础上增加了一个值!(有序集合)

  • 相当于增加了一个可以用来排序的标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
### 添加值,获取值
127.0.0.1:6379> zadd myset 1 one
(integer) 1
127.0.0.1:6379> zadd myset 2 two
(integer) 1
127.0.0.1:6379> zadd myset 3 three
(integer) 1
127.0.0.1:6379> zrange myset 0 -1
1) "one"
2) "two"
3) "three"


### 排序(升序)
127.0.0.1:6379> zadd salary 2500 xiaohong
(integer) 1
127.0.0.1:6379> zadd salary 2200 zhangsna
(integer) 1
127.0.0.1:6379> zadd salary 200 zhuuu
(integer) 1
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf
1) "zhuuu"
2) "zhangsna"
3) "xiaohong"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores
1) "zhuuu"
2) "200"
3) "zhangsna"
4) "2200"
5) "xiaohong"
6) "2500"

### 排序(降序)
127.0.0.1:6379> ZREVRANGE salary 0 -1
1) "zhangsna"
2) "zhuuu"


### 移除一个元素
127.0.0.1:6379> zrem salary xiaohong
(integer) 1

### 获取zset中的个数
127.0.0.1:6379> zcard salary
(integer) 2

### 获取指定区间的数量
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> zadd myset 1 hello
(integer) 1
127.0.0.1:6379> zadd myset 2 world 3 zhuuu
(integer) 2
127.0.0.1:6379> zcount myset 1 3
(integer) 3

应用:

  • set 排序 存储班级成绩表 工资表排序

  • 设置消息的权重,(普通消息1, 重要消息2)

  • 排行榜应用实现(top10实现)

mark

除此外:

2. 三大特殊类型

2.1 geospatial

  • 朋友圈定位
  • 附近的人
  • 打车距离计算

Geo 在 redis 3.2版本就已经推出,这个功能可以推算地理位置的信息,两地之间的距离

只有以下六个命令:http://redis.cn/commands/geoadd.html

geoadd:添加地址位置

1
2
3
4
5
6
7
8
9
10
### 一般通过java程序一次性导入(经度,纬度)

127.0.0.1:6379> geoadd china:city 116.408 39.904 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 121.445 31.213 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 118.769 32.048 nanjing
(integer) 1
127.0.0.1:6379> geoadd china:city 120.165 30.319 hangzhou
(integer) 1

geopos:返回经度和纬度

1
2
3
4
### 获得当前定位
127.0.0.1:6379> geopos china:city nanjing
1) 1) "118.7690010666847229"
2) "32.04799918285738158"

geodist:返回两地之间的距离

返回两个给定位置之间的距离。

如果两个位置之间的其中一个不存在, 那么命令返回空值。

指定单位的参数 unit 必须是以下单位的其中一个:

  • m 表示单位为米。
  • km 表示单位为千米。
  • mi 表示单位为英里。
  • ft 表示单位为英尺。
1
2
3
4
5
6
7
8
9
10
127.0.0.1:6379> geodist china:city nanjing shanghai
"269897.4454"
127.0.0.1:6379> geodist china:city nanjing shanghai km
"269.8974"
127.0.0.1:6379> geodist china:city nanjing shanghai m
"269897.4454"


### 附近的人(获得所有附近人的地址,定位)
### 附件的人:一般通过半径来查询

georadius: 以给定的经度纬度为中心,通过半径来查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km
1) "hangzhou"
2) "nanjing"

127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withdist
1) 1) "hangzhou"
2) "977.8811"
2) 1) "nanjing"
2) "866.0078"
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withcoord
1) 1) "hangzhou"
2) 1) "120.16499966382980347"
2) "30.31899997732214302"
2) 1) "nanjing"
2) 1) "118.7690010666847229"
2) "32.04799918285738158"

### 获得指定数量的人 (count)
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withdist withcoord count 1
1) 1) "nanjing"
2) "866.0078"
3) 1) "118.7690010666847229"
2) "32.04799918285738158"

GEORADIUSBYMEMBER : 找出指定元素周围的其他元素

1
2
3
4
5
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 4000 km
1) "hangzhou"
2) "shanghai"
3) "nanjing"
4) "beijing"

geohash:了解即可

该命令将返回11个字符的Geohash字符串,所以没有精度Geohash,损失相比,使用内部52位表示。返回的geohashes具有以下特性:

  1. 他们可以缩短从右边的字符。它将失去精度,但仍将指向同一地区。

  2. 将二维的经纬度转换成11为的geohash字符串!

1
2
3
127.0.0.1:6379> GEOHASH china:city beijing nanjing
1) "wx4g0bm9xh0"
2) "wtsqqdhvnk0"

底层的实现原理其实就是Zset!!!

1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> zrange china:city 0 -1
1) "hangzhou"
2) "shanghai"
3) "nanjing"
4) "beijing"
127.0.0.1:6379> zrem china:city beijing
(integer) 1
127.0.0.1:6379> zrange china:city 0 -1
1) "hangzhou"
2) "shanghai"
3) "nanjing"

2.2 Hyperloglog

  1. 什么是基数?(不重复的元素)

A{1,3,5,7,8,9}

B{1,3,5,7,8}

基数(不重复的元素) = 5,可以接受误差!

  1. Redis 2.8.9更新hyperloglog (数据结构)

应用:网站UV (一个人访问一次网站多次,但是还是算作一个人)

传统方式

  • set:保存用户的id,然后就可以统计set中的元素数量
  • 这个方式如果保存大量的用户id,就会比较麻烦
  • 目的是为了计数,而不是保存用户id

hyperloglog

  • 占用内存是固定的,2^64不同元素的计数,只需要12kb的内存
  • 所以说hyperloglog是首选
  • 官方说有:百分之0.81的错误率
1
2
3
4
127.0.0.1:6379> pfadd mykey a b c d e f g h i j
(integer) 1
127.0.0.1:6379> pfcount mykey
(integer) 10

测试统计基数:

1
2
3
4
5
6
7
8
9
10
11
12
13
127.0.0.1:6379> pfadd mykey a b c d e f g h i j
(integer) 1 # 创建mykey 元素

127.0.0.1:6379> pfcount mykey # 统计 mykey 数量
(integer) 10
127.0.0.1:6379> pfadd mykey2 i j z x c v b n m
(integer) 1 # 创建mykey2 元素
127.0.0.1:6379> pfcount mykey2
(integer) 9 # 统计 mykey2 数量
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2
OK ## 合并两组 mykey + mykey2 -> mykey3
127.0.0.1:6379> pfcount mykey3
(integer) 15

如果可以容错:那么一定要使用hyperloglog

如果不可以容错:用set或者map都可以

2.3 Bitmap

两个状态都可以使用BitMap!

位存储:

1
0 1 0 1

统计疫情感染人数:

0 1 0 1 0 ……

统计用户信息:活跃,不活跃!

统计用户登录,未登录!

BitMap:位图(数据结构),都是操作二进制位来进行记录,只有0和1两个状态

测试 : 用bitmap记录一周是否有打卡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
127.0.0.1:6379> setbit sign 0 0
(integer) 0
127.0.0.1:6379> setbit sign 1 0
(integer) 0
127.0.0.1:6379> setbit sign 2 0
(integer) 0
127.0.0.1:6379> setbit sign 3 0
(integer) 0
127.0.0.1:6379> setbit sign 4 0
(integer) 0
127.0.0.1:6379> setbit sign 5 1
(integer) 0
127.0.0.1:6379> setbit sign 6 1
(integer) 0

查看某一天是否有打卡:

1
2
3
4
127.0.0.1:6379> getbit sign 3
(integer) 0
127.0.0.1:6379> getbit sign 6
(integer) 1

统计操作:统计打卡的天数

1
2
127.0.0.1:6379> bitcount sign
(integer) 2
打赏
  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!
  • © 2019-2022 Zhuuu
  • PV: UV:

请我喝杯咖啡吧~

支付宝
微信