记录生活中的点点滴滴

0%

redis学习1:基础知识

这一篇文章写一下redis的基础学习,内容有:什么是redis、安装、redis一些基本的特点以及它的数据类型。

什么是redis

  • 基本概念

    redis是一个开源的、使用C语言编写的、支持网络交互的、可基于内存也可持久化的Key-Value数据库(非关系性数据库)。

  • redis的优势

  1. 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
  2. 支持丰富数据类型,支持string,list,set,sorted set,hash
  3. 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
  4. 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除

redis入门

linux上安装redis

参考我以前的博客:CentOS7安装redis

测试性能

redis的bin目录下的 redis-benchmark 可以用于redis的性能测试。

它的基本命令:

1
redis-benchmark [option] [option value]

参数如下:

我们可以测试本机redis的性能,用100个并发连接、100000个请求数看看,命令如下:

1
./redis-benchmark -h localhost -p 6379 -c 100 -n 100000

结果如下,很长,截取一部分图:

如何分析?

基础知识

redis默认16个数据库,默认是第0个。可以使用select切换数据库。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@localhost bin]# ./redis-cli
127.0.0.1:6379> select 3 # 切换数据库
OK
127.0.0.1:6379[3]> DBSIZE # 查看DB大小
(integer) 0
127.0.0.1:6379[3]> set name zs # 插入数据
OK
127.0.0.1:6379[3]> get name # 根据key获取数据
"zs"
127.0.0.1:6379[3]> keys * # 查看所有的key
1) "name"
127.0.0.1:6379[3]> flushdb # 清空当前数据库
OK
127.0.0.1:6379[3]> keys *
(empty list or set)
127.0.0.1:6379[3]>

清空所有数据库内容:FLUSHALL

注意:redis是单线程的。

为什么?

redis是很快的,官方表示,redis是基于内存操作,CPU不再是redis性能瓶颈,redis的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程实现,那就使用单线程了。

redis是C语言写的,官方提供的 100000+ 的QPS,完全不比同样是 key-value 的 Memecache 差

redis为什么单线程还这么快?

1、误区1:高性能的服务器一定是多线程?

2、误区2:多线程(CPU上下文切换)一定比单线程效率高!

核心:redis是将所有的数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU上下文切换:耗时的操作!!!),对于内存系统来说,如果没有上下文切换效率就是最高的。多次读写都是在一个CPU上的,在内存情况下,这个就是最佳的方案。

五大数据类型

官方截图:

它有三个作用:作数据库缓存消息中间件

Redis-key

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
127.0.0.1:6379> FLUSHALL # 清空所有数据
OK
127.0.0.1:6379> keys * # 查看数据库下的所有key
(empty list or set)
127.0.0.1:6379> set name gs # 设置key-value
OK
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379> set age 20
OK
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> EXISTS name # 判断是否存在key
(integer) 1
127.0.0.1:6379> EXISTS name1
(integer) 0
127.0.0.1:6379> move name 1 # 移除key-value
(integer) 1
127.0.0.1:6379> keys *
1) "age"
127.0.0.1:6379> set name gs
OK
127.0.0.1:6379> clear
127.0.0.1:6379> get name
"gs"
127.0.0.1:6379> EXPIRE name 10 # 设置过期时间,这个就是让name10s过期
(integer) 1
127.0.0.1:6379> ttl name # 查看name的生存时间
(integer) 8
127.0.0.1:6379> ttl name
(integer) 7
127.0.0.1:6379> ttl name
(integer) 2
127.0.0.1:6379> ttl name
(integer) 1
127.0.0.1:6379> ttl name # 说明name已经过期
(integer) -2
127.0.0.1:6379> ttl name
(integer) -2
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> keys *
1) "age"
127.0.0.1:6379>
1
2
3
4
5
6
7
127.0.0.1:6379> set name gs
OK
127.0.0.1:6379> type name # 查看key数据类型
string
127.0.0.1:6379> type age
string
127.0.0.1:6379>

String

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
127.0.0.1:6379> FLUSHALL
OK
127.0.0.1:6379> set name gs # 设置key-value
OK
127.0.0.1:6379> keys * # 查看所有key
1) "name"
127.0.0.1:6379> APPEND name GS # 往对应key的value后面追加字符串
(integer) 4
127.0.0.1:6379> APPEND name2 sun # 如果key不存在可创建
(integer) 3
127.0.0.1:6379> keys *
1) "name"
2) "name2"
127.0.0.1:6379> get name
"gsGS"
127.0.0.1:6379> get name2
"sun"
127.0.0.1:6379> STRLEN name # 查看key的value长度
(integer) 4
127.0.0.1:6379>
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> FLUSHALL
OK
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> incr views # 自增1
(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> decr views # 自减1
(integer) 0
127.0.0.1:6379> decr views # 自减1
(integer) -1
127.0.0.1:6379> INCRBY views 10 # 自增10
(integer) 9
127.0.0.1:6379> INCRBY views 10 # 自增10
(integer) 19
127.0.0.1:6379>
1
2
3
4
5
6
7
8
9
10
# 字符串范围,用getrange
127.0.0.1:6379> set name gaosong
OK
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379> GETRANGE name 0 3 # 截取字符串[0,3]
"gaos"
127.0.0.1:6379> GETRANGE name 0 -1 # 获取全部字符串 和 get key一样
"gaosong"
127.0.0.1:6379>
1
2
3
4
5
6
7
8
9
10
# 替换,用setrange
127.0.0.1:6379> set name abcdefg
OK
127.0.0.1:6379> get name
"abcdefg"
127.0.0.1:6379> SETRANGE name 1 xx # 替换指定位置开始的字符串
(integer) 7
127.0.0.1:6379> get name
"axxdefg"
127.0.0.1:6379>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# setex(set with expire) 设置过期时间
# sexnx(set if not exist) 若不存在则设置(在分布式锁中常常用到)
127.0.0.1:6379> setex name 10 "gs" # 设置name的值为gs,10s过期
OK
127.0.0.1:6379> get name
"gs"
127.0.0.1:6379> ttl name
(integer) 5
127.0.0.1:6379> setnx mykey "redis" # 如果mykey不存在,则创建
(integer) 1
127.0.0.1:6379> keys *
1) "mykey"
127.0.0.1:6379> setnx mykey "mongodb" # mykey存在,创建失败
(integer) 0
127.0.0.1:6379> get mykey
"redis"
127.0.0.1:6379>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 批量设置key-value
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 # 设置三组key-value:k1-v1 k2-v2 k3-v3
OK
127.0.0.1:6379> keys *
1) "k1"
2) "k2"
3) "k3"
127.0.0.1:6379> mget k1 k2 k3 # 获取三组value,k1 k2 k3
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx k1 v1 k4 v4 # msetnx 是原子操作,要么全部成功,要么全部失败
(integer) 0
127.0.0.1:6379> get k4
(nil)
1
2
3
4
5
6
7
8
9
10
# 对象
set user:1{name:zhangsan,age:3} # 设置一个user: 1 对象 值为 json字符串来保存一个对象!

# 这里的key-value是一个巧妙的设计:user:{id}:{filed},如此设计在redis中完全OK
127.0.0.1:6379> mset user:1:name zhangsan user:1:age 20
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "zhangsan"
2) "20"
127.0.0.1:6379>
1
2
3
4
5
6
7
8
getset # 先get后set
127.0.0.1:6379> getset name redis # 如果不存在,则返回nil
(nil)
127.0.0.1:6379> getset name mongedb # 如果存在值,获取原来的值,并设置新的值
"redis"
127.0.0.1:6379> get name
"mongedb"
127.0.0.1:6379>

String的使用场景:value除了是我们的字符串还可以是我们的数字

  • 计数器
  • 统计多单位的数量
  • 粉丝数
  • 对象缓存存储

List

基本的数据类型:列表

在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
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中的值LPOP list
"right"
127.0.0.1:6379> rpop list
"one"
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
127.0.0.1:6379>
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> LRANGE list 0 1
1) "three"
2) "two"
127.0.0.1:6379> LPUSH list right # 将一个值或多个值放到列表尾部
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "right"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379> LPOP list # 移除列表第一个元素
"right"
127.0.0.1:6379> rpop list # 移除列表最后一个元素
"one"
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
127.0.0.1:6379>
1
2
3
4
5
6
7
8
9
lindex: # 通过下标读取值
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
127.0.0.1:6379> lindex list 1 # 通过下标读取值
"two"
127.0.0.1:6379> lindex list 0
"three"
127.0.0.1:6379>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
lrem: # 移除指定的值
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
127.0.0.1:6379> lpush list two
(integer) 3
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "three"
3) "two"
127.0.0.1:6379> lrem list 2 two # 移除list的two元素,精确匹配,移除两次
(integer) 2
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
127.0.0.1:6379>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ltrim: # 截取,会改变list
127.0.0.1:6379> FLUSHALL
OK
127.0.0.1:6379> lpush list hello1
(integer) 1
127.0.0.1:6379> lpush list hello2
(integer) 2
127.0.0.1:6379> lpush list hello3
(integer) 3
127.0.0.1:6379> lpush list hello4
(integer) 4
127.0.0.1:6379> ltrim list 1 2 # 通过下标截取指定的长度,这个list已经改变了,只剩下了截取的元素
OK
127.0.0.1:6379> lrange list 0 -1
1) "hello3"
2) "hello2"
127.0.0.1:6379>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
rpoplpush: # 移除列表中的最后一个元素,将它移动到新的列表中
127.0.0.1:6379> lrange list 0 -1
1) "hello3"
2) "hello2"
127.0.0.1:6379> lrange list 0 -1
1) "hello3"
2) "hello2"
127.0.0.1:6379> RPOPLPUSH list mylist
"hello2"
127.0.0.1:6379> lrange mylist 0 -1 # 查看目标列表
1) "hello2"
127.0.0.1:6379> lrange list 0 -1 # 查看原列表
1) "hello3"
127.0.0.1:6379>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
lset: # 将列表中指定位置的值替换为另一个值,更新操作,如果不存在会报错
127.0.0.1:6379> FLUSHALL
OK
127.0.0.1:6379> lpush list one
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "one"
127.0.0.1:6379> lset list 0 newOne # 将list的第1个元素更新为 newOne
OK
127.0.0.1:6379> lrange list 0 -1
1) "newOne"
127.0.0.1:6379> lset list 1 two # list列表中还没有第二个元素,会报错
(error) ERR index out of range
127.0.0.1:6379>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
linsert: # 将某个具体的值插入到某个元素的前面或者后面
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> lpush list hello
(integer) 1
127.0.0.1:6379> lpush list world
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "world"
2) "hello"
127.0.0.1:6379> linsert list before "hello" "you" # 将you插入到list列表的hello前面
(integer) 3
127.0.0.1:6379> linsert list after "hello" "me" # 将me插入到list列表的hello后面
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "world"
2) "you"
3) "hello"
4) "me"
127.0.0.1:6379>

它实际上是一个链表。

Set

set中的值不能重复且无序!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
127.0.0.1:6379> sadd myset "hello" # 往set集合中添加值
(integer) 1
127.0.0.1:6379> sadd myset "world"
(integer) 1
127.0.0.1:6379> sadd myset "GS"
(integer) 1
127.0.0.1:6379> SMEMBERS myset # 查看set集合中的所有值
1) "GS"
2) "hello"
3) "world"
127.0.0.1:6379> SISMEMBER myset GS # 判断一个值是否存在指定的set集合中
(integer) 1
127.0.0.1:6379> SISMEMBER myset gs
(integer) 0
127.0.0.1:6379>
1
2
3
127.0.0.1:6379> scard myset # 获取set集合中的元素个数
(integer) 3
127.0.0.1:6379>
1
2
3
4
5
6
7
8
9
s rem: #移除
127.0.0.1:6379> srem myset hello # 移除集合myset中的hello元素
(integer) 1
127.0.0.1:6379> scard myset
(integer) 2
127.0.0.1:6379> SMEMBERS myset
1) "GS"
2) "world"
127.0.0.1:6379>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
srandmember: # 随机抽取一个元素
127.0.0.1:6379> sadd myset "one"
(integer) 1
127.0.0.1:6379> sadd myset "two"
(integer) 1
127.0.0.1:6379> sadd myset "three"
(integer) 1
127.0.0.1:6379> SMEMBERS myset
1) "two"
2) "GS"
3) "one"
4) "world"
5) "three"
127.0.0.1:6379> SRANDMEMBER myset # 随机在myset集合中抽取一个元素
"three"
127.0.0.1:6379> SRANDMEMBER myset
"two"
127.0.0.1:6379> SRANDMEMBER myset 2 # 随机在myset集合中抽取两个元素
1) "GS"
2) "two"
127.0.0.1:6379> SRANDMEMBER myset 2
1) "world"
2) "one"
127.0.0.1:6379>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
spop: # 随机删除指定元素
127.0.0.1:6379> SMEMBERS myset
1) "world"
2) "GS"
3) "two"
4) "one"
5) "three"
127.0.0.1:6379> spop myset # 随机删除myset集合中一个元素
"three"
127.0.0.1:6379> spop myset
"world"
127.0.0.1:6379> SMEMBERS myset
1) "GS"
2) "two"
3) "one"
127.0.0.1:6379>
1
2
3
4
5
6
7
8
9
smove: # 指定某一元素,从某一set集合移动到另一set集合
127.0.0.1:6379> smove myset myset2 GS # 将myset中的GS移动到myset2中
(integer) 1
127.0.0.1:6379> SMEMBERS myset # 查看myset中元素
1) "two"
2) "one"
127.0.0.1:6379> SMEMBERS myset2 # 查看myset2中的元素
1) "GS"
127.0.0.1:6379>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 # 查看key1中的值
1) "b"
2) "c"
3) "a"
127.0.0.1:6379> SMEMBERS key2 # 查看key2的中的值
1) "d"
2) "c"
3) "e"
127.0.0.1:6379>
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
- 差集 
- 交集
- 并集
# 先往两个set集合中插入值,key1 对应 a b c,key2对应 c d e
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) "c"
3) "a"
127.0.0.1:6379> SMEMBERS key2
1) "d"
2) "c"
3) "e"
# 差集 交集 并集
127.0.0.1:6379> SDIFF 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) "c"
2) "a"
3) "b"
4) "d"
5) "e"
127.0.0.1:6379>

Hash

hash集合:key-value!只是这个时候值是一个map集合。本质和string没有太大区别,还是一个简单的key-vlaue。

1
2
3
4
5
6
7
8
9
10
11
12
127.0.0.1:6379> hset myhash filed1 gs # set一个具体的key-value
(integer) 1
127.0.0.1:6379> hget myhash filed1
"gs"
127.0.0.1:6379> hmset myhash filed1 GS filed2 20 # 设置多个key-value
OK
127.0.0.1:6379> hgetall myhash # 获取全部数据
1) "filed1"
2) "GS"
3) "filed2"
4) "20"
127.0.0.1:6379>
1
2
3
4
5
6
7
hdel: # 删除某个键值对
127.0.0.1:6379> hdel myhash filed1 # 删除某个key-value
(integer) 1
127.0.0.1:6379> hgetall myhash
1) "filed2"
2) "20"
127.0.0.1:6379>
1
2
3
4
hlen: # 或者字段数量
127.0.0.1:6379> hlen myhash # 获取myhash的字段数量
(integer) 1
127.0.0.1:6379>
1
2
3
4
5
6
hexists: # 判断hash中的指定字段是否存在
127.0.0.1:6379> HEXISTS myhash filed2 # 判断myhash中是否存在filed2
(integer) 1
127.0.0.1:6379> HEXISTS myhash filed1 # 判断myhash中是否存在filed1
(integer) 0
127.0.0.1:6379>
1
2
3
4
5
6
7
8
9
hkeys: # 只获取所有keys
hvals: # 只获取所有values
127.0.0.1:6379> hkeys myhash # 获取myhash中所有键
1) "filed2"
2) "filed1"
127.0.0.1:6379> hvals myhash # 获取myhash中所有值
1) "20"
2) "GS"
127.0.0.1:6379>
1
2
3
4
5
6
7
8
9
10
11
12
# string中的incr decr 还有setnx
127.0.0.1:6379> hset myhash filed3 5
(integer) 1
127.0.0.1:6379> HINCRBY myhash filed3 2 # 设置myhash的filed3字段加2
(integer) 7
127.0.0.1:6379> HINCRBY myhash filed3 -2 # 设置myhash的filed3字段加-2,即减2
(integer) 5
127.0.0.1:6379> hsetnx myhash filed4 hello # 若不存在则设置
(integer) 1
127.0.0.1:6379> hsetnx myhash filed4 world # filed4已经存在,设置失败
(integer) 0
127.0.0.1:6379>
1
2
3
4
5
6
7
8
# 当时学习string时我们可以用 mset user:1:name zhangsan user:1:age 20 来设置对象
# 并不是最优的,这里用hash更好
127.0.0.1:6379> hmset user:1 name GS age 20 # 设置user:1的name属性为GS,age属性为20
OK
127.0.0.1:6379> hmget user:1 name age # 获取user:1的name和age属性
1) "GS"
2) "20"
127.0.0.1:6379>

hash更适合存变更的数据,user name age,尤其是用户信息之类的。hash更适合对象的存储。

Zset

有序集合(zset)。

在set的基础上,增加了一个值。set k1 v1 zset k1 score1 v1

1
2
3
4
5
6
7
8
9
127.0.0.1:6379> zadd myset 1 one # 添加一个值
(integer) 1
127.0.0.1:6379> zadd myset 2 two 3 three # 添加多个值
(integer) 2
127.0.0.1:6379> zrange myset 0 -1
1) "one"
2) "two"
3) "three"
127.0.0.1:6379>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 根据score排序
127.0.0.1:6379> zadd salary 2000 zs # 添加三个用户
(integer) 1
127.0.0.1:6379> zadd salary 1500 ls
(integer) 1
127.0.0.1:6379> zadd salary 5000 ww
(integer) 1
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf # 从负无穷到正无穷显示全部用户
1) "ls"
2) "zs"
3) "ww"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores # 从负无穷到正无穷显示全部用户和score
1) "ls"
2) "1500"
3) "zs"
4) "2000"
5) "ww"
6) "5000"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf 3000 # 从负无穷到3000显示全部用户
1) "ls"
2) "zs"
127.0.0.1:6379>
1
2
3
4
5
6
7
8
9
10
11
zrem: # 移除指定元素
127.0.0.1:6379> zrange salary 0 -1
1) "ls"
2) "zs"
3) "ww"
127.0.0.1:6379> zrem salary ls # 移除有序集合中的指定元素
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "zs"
2) "ww"
127.0.0.1:6379>
1
2
3
4
zcard: #获取集合中个数
127.0.0.1:6379> zcard salary # 获取salary中元素个数
(integer) 2
127.0.0.1:6379>
1
2
3
4
5
6
zcount: # 获取指定区间的元素数量
127.0.0.1:6379> zcount salary 1000 3000 # 获取薪水在1000到3000的成员数量
(integer) 1
127.0.0.1:6379> zcount salary 1000 5000 # 获取薪水在1000到5000的成员数量
(integer) 2
127.0.0.1:6379>

可以应用到很多地方:根据工资表排序、成绩、消息不同权重、排行榜等。

三种特殊数据类型

geospatial 地理位置

这个功能可以推算地理位置的信息,两地之间的距离,附近的人等。

只有六个命令:

GEOADD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# geoadd 添加地理位置
# 规则:两极无法添加,我们一般会下载城市数据,直接通过java程序一次性导入
# 参数:key 值(维度)
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqing
(integer) 1
127.0.0.1:6379> geoadd china:city 114.05 22.52 shenzhen
(integer) 1
127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzhou
(integer) 1
127.0.0.1:6379> geoadd china:city 108.96 34.26 xian
(integer) 1
127.0.0.1:6379>

GEOPOS

1
2
3
4
5
6
7
8
9
10
# geopos 获取指定的城市的经度和纬度
127.0.0.1:6379> geopos china:city beijing
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
127.0.0.1:6379> geopos china:city beijing chongqing
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
127.0.0.1:6379>

GEODIST

两个人之间的距离

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

  • m 表示单位为米。
  • km 表示单位为千米。
  • mi 表示单位为英里。
  • ft 表示单位为英尺。

GEODIST 默认使用米作为单位。

1
2
3
4
5
127.0.0.1:6379> geodist china:city beijing shanghai
"1067378.7564"
127.0.0.1:6379> geodist china:city beijing shanghai km
"1067.3788"
127.0.0.1:6379>

GEORADIUS

以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。

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
# 经度110 维度30 周围1000km的城市 
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
# 周围500km的城市
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km
1) "chongqing"
2) "xian"
# 后面还附加了距离
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist
1) 1) "chongqing"
2) "341.9374"
2) 1) "xian"
2) "483.8340"
# 后面还附加了这个城市的经度纬度
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withcoord
1) 1) "chongqing"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
2) 1) "xian"
2) 1) "108.96000176668167114"
2) "34.25999964418929977"
# 限制1个 (实际中肯定不可能都显示,因为太多了,通过count值设置显示的个数)
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km count 1
1) "chongqing"
127.0.0.1:6379>

GEORADIUSBYMEMBER

这个命令和 GEORADIUS 命令一样,都可以找出位于指定范围内的元素,但是 GEORADIUSBYMEMBER 的中心点是由给定的位置元素决定的,而不是像GEORADIUS 那样,使用输入的经度和纬度来决定中心点指定成员的位置被用作查询的中心。

1
2
3
4
5
6
7
8
9
10
11
12
# 以北京为中心,周围1000km距离的城市
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1000 km
1) "beijing"
2) "xian"
# 以北京为中心,周围1500km距离的城市
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1500 km
1) "chongqing"
2) "xian"
3) "hangzhou"
4) "shanghai"
5) "beijing"
127.0.0.1:6379>

GEOHASH

返回一个或多个位置元素的 Geohash 表示。

1
2
3
4
5
# 将二维的字符串转换成一维的字符串,如果两个字符串越接近则越近
127.0.0.1:6379> GEOHASH china:city beijing shanghai
1) "wx4fbxxfke0"
2) "wtw3sj5zbj0"
127.0.0.1:6379>

GEO 底层的实现原理其实就是Zset,我们可以使用Zset命令操作GEO。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 查看有序表中元素内容
127.0.0.1:6379> zrange china:city 0 -1
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
5) "shanghai"
6) "beijing"
# 移除 beijing
127.0.0.1:6379> zrem china:city beijing
(integer) 1
# 再次查看
127.0.0.1:6379> zrange china:city 0 -1
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
5) "shanghai"
127.0.0.1:6379>

Hyperloglog

什么是基数

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

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

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

简介

Redis 2.8.9 版本就更新了 hyperloglog 数据结构!

redis hyperloglog 基数统计的算法。

优点:占用内存固定,2^64不同的元素,只需用掉12KB内存!内存方面hyperloglog首选。

网页的UV(一个人访问一个网站多次,但还算是一个人)

传统的方式:set保存用户的id,然后统计set中的元素数量来判断。

这个方式保存大量的用户id,就会比较麻烦,我们的目的是为了计数,而不是保存用户id。

0.81%错误率!统计UV任务,这个误差可以忽略不计。

测试使用

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
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
127.0.0.1:6379> PFCOUNT mykey2 # 统计mykey2元素的基数数量
(integer) 9
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2 # 将这两组元素合并为mykey3 并集
OK
127.0.0.1:6379> PFCOUNT mykey3 # 统计mykey3元素的基数数量
(integer) 15
127.0.0.1:6379>

Bitmaps

位存储

统计用户信息:活跃、不活跃!打卡信息;登录信息;等等

两个状态的,都可以用到Bitmaps!

Bitmaps,位图,数据结构!都是二进制位来进行记录,就只有0和1两种状态。

测试

打卡记录,0代表未打卡,1代表打卡了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 1
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> setbit sign 5 1
(integer) 0
127.0.0.1:6379> setbit sign 6 1
(integer) 0
127.0.0.1:6379> setbit sign 0 1
(integer) 0
127.0.0.1:6379>

查看某一天是否打卡:

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

统计打卡天数:

1
2
3
127.0.0.1:6379> bitcount sign
(integer) 5
127.0.0.1:6379>