Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。

通常在我们java中,我们都是当作缓存来使用

官方文档
中文文档
菜鸟教程

基本命令

常用命令

redis 默认是有16个数据库

  • select [index] 切换数据库
  • set [key] [value] 设置键值对
  • get [key] 获取当前key所对应的value
  • flushdb 清空当前数据库中的数据
  • flushall 清空所有数据库中的数据
  • dbsize 查看当前数据库的大小
  • keys * 查看当前数据库中所有的 key
  • del [key] 删除当前key
  • type [key] 查看 key 的类型
  1. 127.0.0.1:6379> select 1 #切换数据库
  2. OK
  3. 127.0.0.1:6379[1]> set a 123 # 设置键值对 set key value
  4. OK
  5. 127.0.0.1:6379[1]> get a # 获取 key a 对应的 value
  6. "123"
  7. 127.0.0.1:6379[1]> flushdb # 清空当前数据库
  8. OK
  9. 127.0.0.1:6379[1]> dbsize # 查看当前数据库的大小
  10. (integer) 0
  11. 127.0.0.1:6379[1]> select 0 # 切换到第0个数据库
  12. OK
  13. 127.0.0.1:6379> set a 213 # 在第0个数据库设置 a = 213
  14. OK
  15. 127.0.0.1:6379> keys * # 查看当前数据库中所有的 key
  16. 1) "a"
  17. 127.0.0.1:6379> select 1
  18. OK
  19. 127.0.0.1:6379[1]> set a 213 # 在第1个数据库设置 a = 213
  20. OK
  21. 127.0.0.1:6379[1]> dbsize
  22. (integer) 1
  23. 127.0.0.1:6379[1]> flushall #清空所有数据库里的数据
  24. 31:M 02 Apr 10:36:22.223 * DB saved on disk
  25. OK
  26. 127.0.0.1:6379[1]> dbsize
  27. (integer) 0
  28. 127.0.0.1:6379> select 0
  29. OK
  30. 127.0.0.1:6379> dbsize
  31. (integer) 0

判断 key 是否存在?
返回1 代表存在
返回0 代表不存在

  1. 127.0.0.1:6379> EXISTS name
  2. (integer) 0
  3. 127.0.0.1:6379> set name hello
  4. OK
  5. 127.0.0.1:6379> EXISTS name
  6. (integer) 1
  7. 127.0.0.1:6379> del name # 删除当前 key
  8. (integer) 1
  9. 127.0.0.1:6379> EXISTS name
  10. (integer) 0

将当前数据库的 key 移动到给定的数据库 db 当中。
move [key] [db]
返回1 代表移动成功
返回0 代表移动失败

  1. 127.0.0.1:6379> set name hello
  2. OK
  3. 127.0.0.1:6379> get name
  4. "hello"
  5. 127.0.0.1:6379> move name 1 # 移动到 数据库1
  6. (integer) 1
  7. 127.0.0.1:6379> select 1
  8. OK
  9. 127.0.0.1:6379[1]> get name
  10. "hello"

查看 key 的类型
type [key]

  1. 127.0.0.1:6379> set name hello
  2. OK
  3. 127.0.0.1:6379> type name
  4. string

给 key 设置过期时间

expire [key] [seconds] 设置key seconds秒后过期
ttl [key] 查看 key 的存活时间

  1. 127.0.0.1:6379[1]> expire name 10 # 设置 name 这个key 10秒后过期
  2. (integer) 1
  3. 127.0.0.1:6379[1]> ttl name # 查看 当前key的有效时间
  4. (integer) 4
  5. 127.0.0.1:6379[1]> ttl name
  6. (integer) 2
  7. 127.0.0.1:6379[1]> ttl name
  8. (integer) 0
  9. 127.0.0.1:6379[1]> ttl name # 如果为-2 代表key 已经过期
  10. (integer) -2
  11. 127.0.0.1:6379[1]> get name #再次获取,发现为nil
  12. (nil)
  13. 127.0.0.1:6379[1]>

基本数据类型


string

据说90%的java程序员使用redis 都是只用string 类型,哈哈哈。
难道是因为大家都把 对象转为 json 然后往里边存值吗

  • 存取字符串类型
127.0.0.1:6379> set name hello
OK
127.0.0.1:6379> get name
"hello"
  • 追加字符串
    append [key] [value]
    如果key 不存在 就相当于 set key value
127.0.0.1:6379> set name hello
OK
127.0.0.1:6379> get name
"hello"
127.0.0.1:6379> append name redis
(integer) 10 # 返回当前字符串的长度
127.0.0.1:6379> get name
"helloredis"
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379> append age 20 # age 不存在 reis会创建一个 age
(integer) 2
127.0.0.1:6379> get age
"20"
  • 获取字符串的长度
    strlen [key]
127.0.0.1:6379> strlen name
(integer) 10

一般我们会把 文章浏览量啊,点赞数啊,什么的放到redis 里边,这个时候我们可以使用 自加或者自减操作

  • 自加1 和 自减1 操作
    • incr [key] 加一
    • decr [key] 减一
      返回加一 或 减一后的新数值
127.0.0.1:6379> set views 0
OK
127.0.0.1:6379> incr views #加一
(integer) 1 #当前值为1
127.0.0.1:6379> incr views
(integer) 2
127.0.0.1:6379> decr views #减一
(integer) 1 #当前值为1
127.0.0.1:6379> decr views
(integer) 0
  • 自加 n 自减 n
    • incrby [key] [increment]
    • decrby [key] [decrement]
      自加 increment值
      自减 decrement值
127.0.0.1:6379> incrby views 10 # 自加10
(integer) 10
127.0.0.1:6379> decrby views 10 # 自减10
(integer) 0
  • 截取指定长度的字符串
    GETRANGE [key] [start] [end]
    如果end 为 -1 则查询全部
127.0.0.1:6379> get name
"helloredis"
127.0.0.1:6379> GETRANGE name 0 4
"hello"
127.0.0.1:6379> GETRANGE name 0 -1
"helloredis"
  • 从指定位置开始替换字符串
    SETRANGE [key] [offset] [value]
127.0.0.1:6379> set halo "to eat"
OK
127.0.0.1:6379> get halo
"to eat"
127.0.0.1:6379> SETRANGE halo 3 xxx # 从 e 开始替换,包括e
(integer) 6
127.0.0.1:6379> get halo
"to xxx"
  • 设置key 并且指定存活时间
    setex [key] [seconds] [value]
    (set with exipre)
  • 如果当前 key 不存在 则set 成功,如过存在,set 失败
    setnx [key] [value]
    (set if not exist ) 分布式中才使用
127.0.0.1:6379> setex k1 10 v1
OK
127.0.0.1:6379>
127.0.0.1:6379> ttl k1
(integer) 7
127.0.0.1:6379> ttl k1
(integer) 6
127.0.0.1:6379> setnx k2 v2
(integer) 1
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379> setnx k2 vvvvv2
(integer) 0
127.0.0.1:6379> get k2
"v2"
  • 批量存取多个值
    • mset [key value …]
    • mgget [key value …]
    • msetnx [key value …]
      如果不存在,存入成功,存在存入失败,和上面的 setnx 一致
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379> keys k*
1) "k3"
2) "k1"
3) "k2"
127.0.0.1:6379> mget k1 k2 k3
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx k1 v11 k2 v22 k3 v33
(integer) 0
127.0.0.1:6379> mget k1 k2 k3
1) "v1"
2) "v2"
3) "v3"

这可不就相当于 redis 自带一些原子性操作吗

接下来搞点有意思的 虽然一般对象都建议使用 hash才存
但现在我们存取一个对象在string 里
比如说,我们存取一个 user对象,这个对象有name和age还有 id

我们一般都会这样来存,
user:{id}:name v
usr:{id}:age v

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> keys user:1*
1) "user:1:name"
2) "user:1:age"
  • setget 命令,设置当前key 的 新value,返回老value
    setget [key] [value]
127.0.0.1:6379> getset k4 v4 #刚设置 k4,原来的值为空
(nil)
127.0.0.1:6379> get k4 #可以看到 k4 的值已经发生改变
"v4"
127.0.0.1:6379> getset k4 v444 # 上一次的值为 v4
"v4"
127.0.0.1:6379> get k4 # 新值为 v444
"v444"

string 的应用场景
  • 计算器
  • 统计多单位的数量
  • 用户关注数,uid:1:follow 0
    每增加一个关注数
    incr uid:1:follow
  • 对象缓存存储

list

基本的数据类型,列表
在redis 中我们可以list 玩成一个栈,队列,甚至阻塞队列!

  • lpush [key] [value …]
    从左边压入 list 一个或多个值
127.0.0.1:6379> lpush mylist one
(integer) 1
127.0.0.1:6379> lpush mylist two
(integer) 2
127.0.0.1:6379> lpush mylist three
(integer) 3
127.0.0.1:6379> lrange mylist 0 -1 #获取 list 的值,与getrange 一样
1) "three"
2) "two"
3) "one"

先进后出,像不像栈?

  • rpush [key] [value …]
    从 右边 进入队列一个或多个元素
127.0.0.1:6379> rpush mylist rvalue
(integer) 4
127.0.0.1:6379> lrange mylist 0 -1
1) "three"
2) "two"
3) "one"
4) "rvalue"
  • 移除元素
    lpop [key] 从左边移除一个元素
    rpop [key] 从右边移除一个元素
    返回 移除 的元素值
127.0.0.1:6379> LPOP mylist # 从左边开始移除一个元素
"three" #移除 three
127.0.0.1:6379> lrange mylist 0 -1
1) "two"
2) "one"
3) "rvalue"
127.0.0.1:6379> RPOP mylist # 从右边移除一个元素
"rvalue"
127.0.0.1:6379> lrange mylist 0 -1
1) "two"
2) "one"
  • 通过下标获取值
    lindex [key] [index]
127.0.0.1:6379> LINDEX mylist 0
"two"
127.0.0.1:6379> LINDEX mylist 1
"one"
  • 获取 list 的长度
    llen [key]
127.0.0.1:6379> llen mylist
(integer) 2
  • 移除指定值
    lrem [key] [count] [value]
    移除 key 中 count 个 值为 value的 元素
127.0.0.1:6379> lpush mylist one one
(integer) 4
127.0.0.1:6379> LRANGE mylist 0 -1
1) "one"
2) "one"
3) "one"
4) "two"
127.0.0.1:6379> lrem mylist 1 one #移除一个 one
(integer) 1 #移除1个元素
127.0.0.1:6379> lrem mylist 2 one #移除两个 one
(integer) 2 #移除2个元素
  • list 截取
    ltrim [key] [start] [end]
    使list 中只保留 下标start 到 end 之间的元素
127.0.0.1:6379> lpush mylist hello hello2 hello3 hello4
(integer) 4
127.0.0.1:6379> lrange mylist 0 -1
1) "hello4"
2) "hello3"
3) "hello2"
4) "hello"
127.0.0.1:6379> ltrim mylist 1 2
OK
127.0.0.1:6379> lrange mylist 0 -1
1) "hello3"
2) "hello2"
  • 把 一个list的最右边元素 移进另一个list的最左边
    rpoplpush [source] [destination]
127.0.0.1:6379> rpoplpush mylist mylist2
"hello2"
127.0.0.1:6379> lrange mylist2 0 -1
1) "hello2"
2) "hi"
  • 将list 指定下标的值替换
127.0.0.1:6379> lset mylist2 0 item #
OK
127.0.0.1:6379> lrange mylist2 0 -1
1) "item"
2) "hi"
127.0.0.1:6379> lset mylist 3 a #列表中 list 这个key 不存在,报错!
(error) ERR no such key
127.0.0.1:6379> lset mylist2 3 a #没有当前这个 index 下标,报错!
(error) ERR index out of range
  • 为list 中 特定的元素 的前后 插入元素
    LINSERT [key] [before|after] [pivot] [value]
    前 是 l(左)
    后 是 r(右)
127.0.0.1:6379> rpush list1 hello word
(integer) 2
127.0.0.1:6379> lrange list1 0 -1
1) "hello"
2) "word"
127.0.0.1:6379> LINSERT list1 before word redis
(integer) 3
127.0.0.1:6379> lrange list1 0 -1
1) "hello"
2) "redis"
3) "word"
127.0.0.1:6379> LINSERT list1 after word list
(integer) 4
127.0.0.1:6379> lrange list1 0 -1
1) "hello"
2) "redis"
3) "word"
4) "list"

小结:

  • 可以把list想象成 一个链表,可以在 left [node] after 来添加值
  • 如果 key 不存在 创建新链表
  • key 存在,则往该链表中加入 新元素
  • 如果移除了所有值,空链表,也代表不存在

set

set 中的值不能重复且无序

  • sadd [key] [value …] 往set中添加元素
  • Smembers [key] 查看 set 所有的元素(成员)
  • sismember [key] [member] 查看 set 是否存在 member这个值
    存在返回 1 不存在返回0
  • scard [key] 获取set的长度
  • srem [key] [value …] 移除 set 中的指定元素
127.0.0.1:6379> sadd myset hello # 添加一个元素
(integer) 1
127.0.0.1:6379> sadd myset halo geji # 添加多个元素
(integer) 2
127.0.0.1:6379> SMEMBERS myset #查看 myset中所有的元素
1) "halo"
2) "geji"
3) "hello"
127.0.0.1:6379> Sadd myset a b c
(integer) 3
127.0.0.1:6379> SMEMBERS myset 
1) "a"
2) "geji"
3) "hello"
4) "halo"
5) "c"
6) "b"
127.0.0.1:6379> sismember myset hello #myset 中存在 hello 返回1
(integer) 1
127.0.0.1:6379> sismember myset g # myset 中不存在 g,返回0
(integer) 0
127.0.0.1:6379> scard myset # 获取set 的长度
(integer) 6
127.0.0.1:6379> srem myset a b c # 移除set 中的 a b c元素
(integer) 3
127.0.0.1:6379> scard myset
(integer) 3
127.0.0.1:6379> smembers myset
1) "halo"
2) "geji"
3) "hello"
  • 随机筛选出来 set 中的成员
    SRANDMEMBER [key] [count]
127.0.0.1:6379> SRANDMEMBER myset 1
1) "halo"
127.0.0.1:6379> SRANDMEMBER myset 1
1) "geji"
127.0.0.1:6379> SRANDMEMBER myset 2
1) "hello"
2) "geji"
  • 随机删除元素
    spop [key] [count]
127.0.0.1:6379> spop myset
"geji"
127.0.0.1:6379> spop myset
"halo"
  • 将一个指定的值,移动到另一个set中
    smove [source] [destination] [member]
    是不是和 rpoplpush 有点像?
127.0.0.1:6379> sadd myset hello word redis
(integer) 3
127.0.0.1:6379> sadd myset2 set2
(integer) 1
127.0.0.1:6379> smove myset myset2 word # 将 myset 的 word成员移动到 myset2中
(integer) 1
127.0.0.1:6379> SMEMBERS myset2
1) "word"
2) "set2"
127.0.0.1:6379> SMEMBERS myset
1) "redis"
2) "hello"

例子:微博,b站,共同关注!共同爱好,推荐好友(交集)
数字集合类:

  • sdiff [key …] 差集,两个集合不一样的元素
  • sinter [key …] 交集,两个集合中的一样的元素
  • sunion [key …] 并集
127.0.0.1:6379> sadd s1 a b c
(integer) 3
127.0.0.1:6379> sadd s2 a c
(integer) 2
127.0.0.1:6379> sdiff s1 s2 #差集
1) "b"
127.0.0.1:6379> sinter s1 s2 #交集
1) "c"
2) "a"
127.0.0.1:6379> sunion s1 s2 #并集
1) "c"
2) "b"
3) "a"

hash

我们可以把它当成一个 map 集合
key-map 集合,这个时候的key 是 map ,而 map又有自己的 key,vlaue
可以理解为,key< key,value> 想必这个用在集合的<>,大家并不陌生吧

  • hset [key] [filed] [value]
    这里 filed 其实也可以理解为 key对吧
  • hget [key] [filed]
  • hmset [key] [filed …] [value …]
    存入多个值
  • hmget [key] [filed …]
    取出多个值
  • hsetnx [key] [field] [value]
    如果 filed 不存在 则存入
  • hgetall [key]
    获取 key 中所有 k-v(field 和 value)
  • hdel [key] [field]
    删除 key 中的 field
  • hlen [key]
    查看当前hash key 的长度
127.0.0.1:6379> hset myhash filed1 1
(integer) 1
127.0.0.1:6379> hget myhash filed1
"1"
127.0.0.1:6379> hmset myhash2 k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379> hmget myhash2 k1 k2 k3
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> hsetnx myhash2 k1 v111 # 如果 filed 不存在 则存入
(integer) 0 # 存在了,存入失败
127.0.0.1:6379> hget myhash2 k1
"v1"
127.0.0.1:6379> hgetall myhash2 # 获取 key 中所有 k-v(field 和 value)
1) "k1"
2) "v1"
3) "k2"
4) "v2"
5) "k3"
6) "v3"
127.0.0.1:6379> hdel myhash2 k1 # 删除 myhash2 中的 k1
(integer) 1
127.0.0.1:6379> hgetall myhash2
1) "k2"
2) "v2"
3) "k3"
4) "v3"
127.0.0.1:6379> hlen myhash2 # 查看当前 key 的长度
(integer) 2

可以看到,hash 本质上和字符串没有什么区别。还是一个简单的 k-v

  • 判断 hash的 key 中的 field 是否存在
    hexists [key] [field]
127.0.0.1:6379> hexists myhash2 k1
(integer) 0 # 不存在
127.0.0.1:6379> hexists myhash2 k2
(integer) 1 # 存在
  • 获取所有的字段
    hkeys [key]
  • 获取所有的值
    hvals [key]
127.0.0.1:6379> hkeys myhash2
1) "k2"
2) "k3"
127.0.0.1:6379> hvals myhash2
1) "v2"
2) "v3"
  • hash 中的字段 自增或自减
    hincrby [key] [filed] [increment]
    increment 为正数 自增,为负数,自减
127.0.0.1:6379> hset myhash2 views 5
(integer) 1
127.0.0.1:6379> hget myhash2 views
"5"
127.0.0.1:6379> hincrby myhash2 views 2
(integer) 7
127.0.0.1:6379> hincrby myhash2 views -2
(integer) 5

hash 可以放一些变更的数据,如用户信息啊,什么的.
相比于 string 来说,hash 更加适合 对象存储。

127.0.0.1:6379> hset user:1 name hello
(integer) 1
127.0.0.1:6379> hset user:1 age 20
(integer) 1
127.0.0.1:6379> hgetall user:1
1) "name"
2) "hello"
3) "age"
4) "20"

zset

zset 是一个有序的 set 集合

  • zadd [key] [index] [score] [member]
    将 member 添加到 score 这个位置
  • zrange [key] [start] [end]
    获取zset 中成员
127.0.0.1:6379> zadd myzset 1 a 2 b 3 c
(integer) 3
127.0.0.1:6379> zrange myzset 0 -1
1) "a"
2) "b"
3) "c"
127.0.0.1:6379> zadd myzset 5 d 4 e 
(integer) 2
127.0.0.1:6379> zrange myzset 0 -1
1) "a"
2) "b"
3) "c"
4) "e"
5) "d"
127.0.0.1:6379> zadd myzset 2 f 6 g # 将 f 添加到 第三个位置 g添加到 第 7个位置 下标其实算是从 0 算吧
(integer) 2
127.0.0.1:6379> zrange myzset 0 -1
1) "a"
2) "b"
3) "f"
4) "c"
5) "e"
6) "d"
7) "g"
  • 根据score 排序,从最小值到最大值
    ZRANGEBYSCORE [key] [min] [max]
    比如说我们将 score 存为工资,现在从小到大排列
  • zrem [key] [memeber]
    移除成员
127.0.0.1:6379> zadd salary 2000 halo 4000 tutu 500 xbz
(integer) 3
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf # -inf 是负无穷,+inf 是 正无穷,这个意思是,从小到大排列
1) "xbz"
2) "halo"
3) "tutu"
127.0.0.1:6379> ZRANGEBYSCORE salary (500 +inf # 如果带上 ( 则意味着 开区间,  500< n < +inf
1) "halo"
2) "tutu"
127.0.0.1:6379> zrem salary xbz # 移除成员 xbz
(integer) 1
127.0.0.1:6379> ZRANGE salary 0 -1
1) "halo"
2) "tutu"
  • 倒序排序,从大到小
    ZREVRANGE [key] [max] [min]
  • 根据 score 从大到小
    ZREVRANGEBYSCORE [key] [start] [end]
  • zcard [key]
    zset 中有多少个成员
  • 获取指定区间的成员数量,其实我觉得这个用 ZRANGEBYSCORE 也可以实现,就不做演示了
    zcount [key] [min] [max]
127.0.0.1:6379> ZREVRANGE salary 0 -1
1) "tutu"
2) "halo"
127.0.0.1:6379> ZREVRANGEBYSCORE salary +inf -inf
1) "tutu"
2) "halo"
127.0.0.1:6379> zcard salary
(integer) 2

例子:
b站排行榜啊,班级成绩啊

redis 是单线程的

首先要知道一点redis是非常快的,这点我们通过redis的测试命令就可以知道, 但是为什么使用单线程的redis还能这么快?如果使用多线程呢?

redis 是基于内存进行操作,要知道的是,如果使用多线程,cpu在线程之间的切换(上下文切换)也是需要耗费时间的。既然是基于内存操作,本身已经够快了,如果使用多线程的话,还要考虑安全问题,以及切换的时间耗费。对于内存系统来讲,使用单线程就是最快的。

redis 的性能瓶颈并不是cpu 而是内存和网络带宽

三种特殊类型

geospatial 地理位置

这个功能可以推算地理位置
比如说,朋友圈的定位,附近的人,打车距离计算

将指定的地理空间位置(纬度、经度、名称)添加到指定的key中。这些数据将会存储到sorted set 这样的目的是为了方便使用GEORADIUS或者GEORADIUSBYMEMBER命令对数据进行半径查询等操作

别的不说!!这点是真的强,之前做项目的时候还在想,我怎么通过gps来获取两个地理位置之间的距离来判断最小仓库。之前还想调用高德api,但是要钱啊,现在这可不就有方法了吗
通过java程序将数据批量导入。然后直接获取距离啊。舒服啊。唯一的困难点就是经纬度导入问题了,这个问题似乎还要导入第三方接口,当然不怕麻烦的话,好像也可以去网站上找些大概的位置,比如这个网站
然后通过ip来获取城市位置,或者 调用gps 定位 ,最后与仓库的位置做判断。

下面来测试一下啊

  • geoadd [key] [经度] [纬度] [member]
    有效的经度介于-180-180度之间
    有效的纬度介于-85.05112878 度至 85.05112878 度之间。
  • geopos [key] [member]
    获取经纬度
127.0.0.1:6379> geoadd china:city 113.665412 34.757975 zhengzhou
(integer) 1
127.0.0.1:6379> geoadd china:city 116.405285 39.904989 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 106.504962 29.533155 chongqing
(integer) 1
127.0.0.1:6379> geopos china:city zhengzhou
1) 1) "113.66541177034378052"
   2) "34.75797603259534441"

geodist 判断两城市之间的距离

geodist key [member1] [member2] [unit]
m表示单位为米
km表示单位为千米
mi表示单位为英里
ft表示单位为英尺
如果用户没有显式地指定单位参数,那么geodist默认使用米作为单位。
geodist命令在计算距离时会假设地球为完美的球形,在极限情况下,这一假设最大会造成0.5%的误差。

127.0.0.1:6379> geodist china:city zhengzhou chongqing
"889716.0102" # 米
127.0.0.1:6379> geodist china:city zhengzhou chongqing km
"889.7160" # 千米
127.0.0.1:6379> geodist china:city zhengzhou beijing km
"621.5636"

这种距离的误差 是靠你输入的 经纬度来计算的,所以输入的经纬度的准确度很重要

georadius 获取某一半径内的元素

比如微信附近的人,好像就可以通过这种方式来实现吧

  • 获取某一 经纬度周围半径内的元素

georadius [key] [经度] [纬度] [radius] [m|km|ft|mi]

127.0.0.1:6379> georadius china:city 110 30 1000 km
1) "chongqing"
2) "zhengzhou"
127.0.0.1:6379> georadius china:city 110 30 1000 km withdist #带上距离
1) 1) "chongqing"
   2) "341.4052"
2) 1) "zhengzhou"
   2) "631.2281"
127.0.0.1:6379> georadius china:city 110 30 1000 km withcoord #带上坐标
1) 1) "chongqing"
   2) 1) "106.50495976209640503"
      2) "29.53315530684997015"
2) 1) "zhengzhou"
   2) 1) "113.66541177034378052"
      2) "34.75797603259534441"
  • 获取某一城市的 周围半径 内的元素
    GEORADIUSBYMEMBER [key] [memeber] [radius] [m|km|ft|mi]
127.0.0.1:6379> GEORADIUSBYMEMBER china:city zhengzhou 1000 km
1) "zhengzhou"
2) "beijing"
3) "chongqing"

geohash
这个用的少,该命令返回11个字符串的 geohash 字符串。
代表当前城市的经纬度(将二维的地理位置坐标,转换为一维的字符串)
可以用来判断是否在 同一个城市

127.0.0.1:6379> geohash china:city zhengzhou
1) "ww0vdx2xuh0"

GEO 底层的实现原理其实是 zset, 所以我们可以使用 zset的 api

127.0.0.1:6379> zrange china:city 0 -1
1) "chongqing"
2) "zhengzhou"
3) "beijing"

hyperloglog

这是一个求基数的统计算法

当你有大量用户与巨大的数据项进行交互时,并且你想要保存这些交互的数量时,HyperLogLog是一个很好的数据结构,此外,此数据类型可以保持各种集合的计数,并快速计算其并集或交集的结果数值。

每个计数器需要12KB的内存,标准错误为0.81%,可以计算几乎无限量的独立数据项(2 ^ 64)。

举例

  1. YouTube,Reddit,亚马逊的页面浏览量
  2. 用户组的高效联合/交叉
  3. 甚至视频的一个用户一次只能增加一个播放量
  • PFADD [key] [value …]
  • pfcount [key]
    计算个数
  • pfmerge [destkey] [sourcekey …]
    合并成新组
127.0.0.1:6379> PFADD video1 user:1 user:2 user:3 user:4 user:1
(integer) 1
127.0.0.1:6379> pfcount video1 #它计算的时候,会把重复的元素去掉
(integer) 4
127.0.0.1:6379> pfadd video2 user:1 user:5 user:6
(integer) 1
127.0.0.1:6379> pfcount video2
(integer) 3
127.0.0.1:6379> pfmerge videos video1 video2
OK
127.0.0.1:6379> pfcount videos
(integer) 6

感谢这篇文章

bitmaps

位存储 二进制存储,只要是两种状体的信息都可以用 bitmap来存储

比如统计
用户信息,活跃,不活跃
登录,未登录
签到,未签到

  • 设置 bit
    setbit [key] [offset] [value]
  • 获取 bit
    getbit [key] [offset]
    例子:
    用户1,一周内的签到情况
    0 未签到,1 签到
    • bitcount [key]
      获取 值为1的 个数
127.0.0.1:6379> setbit user:1:sign 0 0 # 周日 未签到
(integer) 0
127.0.0.1:6379> setbit user:1:sign 1 0 # 周一 未签到
(integer) 0
127.0.0.1:6379> setbit user:1:sign 2 1 # 周二 签到了
(integer) 0
127.0.0.1:6379> setbit user:1:sign 3 1
(integer) 0
127.0.0.1:6379> setbit user:1:sign 4 1
(integer) 0
127.0.0.1:6379> setbit user:1:sign 5 1
(integer) 0
127.0.0.1:6379> setbit user:1:sign 6 1
(integer) 0
127.0.0.1:6379> getbit user:1:sign 2 # 获取周二签到情况
(integer) 1
127.0.0.1:6379> getbit user:1:sign 1 # 获取周一签到情况
(integer) 0
127.0.0.1:6379> bitcount user:1:sign # 获取一周内签到的天数(为 1 的个数)
(integer) 5