Redis 基本数据类型操作
连接redis 服务器:
ubuntu@mario:/mnt/c/Users/admin$ redis-server
测试连接:
ubuntu@mario:/mnt/c/Users/admin$ redis-cli
127.0.0.1:6379> ping
PONG
127.0.0.1:6379>
性能测试
redis 性能测试,100个并发,每个并发100000个请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000
切换数据库
127.0.0.1:6379> select 3
OK
127.0.0.1:6379[3]> DBSize ## 查看当前数据库大小
(integer) 0
127.0.0.1:6379[3]> dbsize
(integer) 0
127.0.0.1:6379[3]> set name mario
OK
127.0.0.1:6379[3]> get name
"mario"
127.0.0.1:6379[3]> dbsize
(integer) 1
127.0.0.1:6379[3]> select 7
OK
127.0.0.1:6379[7]> get name
(nil)
127.0.0.1:6379[7]> select 3
OK
127.0.0.1:6379[3]> keys * ## 查看数据库所有的key
1) "name"
127.0.0.1:6379[3]> flushdb ## 清除当前数据库
OK
127.0.0.1:6379[3]> flushall ## 清除全部数据库的内容
OK
127.0.0.1:6379[3]> keys *
(empty list or set)
redis 是单线程的,明白Redis是很快的,官方表示,Redis是基于内存操作,CPU不是Redis性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程来实现,就使用单线程了!所有就使用了单线程了!Redis 是C 语言写的,官方提供的数据为 100000+ 的QPS,完全不比同样是使用 key-vale的Memecache差!
Redis 为什么单线程还这么快?
- 1、误区1:高性能的服务器一定是多线程的?
- 2、误区2:多线程(CPU上下文会切换!)一定比单线程效率高!
- 先去CPU>内存>硬盘的速度要有所了解!
核心:redis 是将所有的数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU上下文会切换:耗时的操作!!!),对于内存系统来说,如果没有上下文切换效率就是最高的!多次读写都是在一个CPU上的,在内存情况下,这个就是最佳的方案!
Redis-key
127.0.0.1:6379[3]> keys *
(empty list or set)
127.0.0.1:6379[3]> set age 1
OK
127.0.0.1:6379[3]> get age
"1"
127.0.0.1:6379[3]> set name mario
OK
127.0.0.1:6379[3]> get naem
(nil)
127.0.0.1:6379[3]> get name
"mario"
127.0.0.1:6379[3]> exists name
(integer) 1
127.0.0.1:6379[3]> exists name1
(integer) 0
127.0.0.1:6379[3]> move name 1
(integer) 1
127.0.0.1:6379[3]> get name
(nil)
==================================
127.0.0.1:6379[3]> keys *
(empty list or set)
127.0.0.1:6379[3]> set age 1
OK
127.0.0.1:6379[3]> get age
"1"
127.0.0.1:6379[3]> set name mario
OK
127.0.0.1:6379[3]> get name
"mario"
127.0.0.1:6379[3]> exists name # 判断当前的key是否存在
(integer) 1
127.0.0.1:6379[3]> exists name1
(integer) 0
127.0.0.1:6379[3]> move name 1 # 移除当前的key
(integer) 1
127.0.0.1:6379[3]> get name
(nil)
127.0.0.1:6379[3]> clear #清屏
127.0.0.1:6379[3]> set name xiaoming
OK
127.0.0.1:6379[3]> expire name 10 # 设置key的过期时间,单位是秒
(integer) 1
127.0.0.1:6379[3]> ttl name # 查看当前key的剩余时间
(integer) 7
127.0.0.1:6379[3]> get name
"xiaoming"
127.0.0.1:6379[3]> ttl name
(integer) 3
127.0.0.1:6379[3]> get name
(nil)
127.0.0.1:6379[3]> type age # 查看当前key的一个类型!
string
String 类型
127.0.0.1:6379[3]> set name xiaohong
OK
127.0.0.1:6379[3]> keys *
1) "age"
2) "name"
127.0.0.1:6379[3]> exists key
(integer) 0
127.0.0.1:6379[3]>
127.0.0.1:6379[3]> exists name
(integer) 1
127.0.0.1:6379[3]> append name ",xiaoming" # 追加字符串,如果当前key不存在,就相当于setkey
(integer) 17
127.0.0.1:6379[3]> get name
"xiaohong,xiaoming"
127.0.0.1:6379[3]> strlen name # 获取字符串的长度
(integer) 17
127.0.0.1:6379[3]> append name ",xiaolan"
(integer) 25
127.0.0.1:6379[3]> append sex "boy" # 追加字符串,如果当前key不存在,就相当于setkey
(integer) 3
127.0.0.1:6379[3]> get sex
"boy"
127.0.0.1:6379[3]>
#########################################
# i++
# 步长 i+=
127.0.0.1:6379[3]> set views 0 # 初始浏览量为0
OK
127.0.0.1:6379[3]> get views
"0"
127.0.0.1:6379[3]> incr views # 自增1 浏览量变为1
(integer) 1
127.0.0.1:6379[3]> incr views
(integer) 2
127.0.0.1:6379[3]> get views
"2"
127.0.0.1:6379[3]> decr views # 自减1 浏览量-1
(integer) 1
127.0.0.1:6379[3]> decr views
(integer) 0
127.0.0.1:6379[3]> decr views
(integer) -1
127.0.0.1:6379[3]> get views
"-1"
127.0.0.1:6379[3]> incrby views 10 # 可以设置步长,指定增量!
(integer) 9
127.0.0.1:6379[3]> incrby views 10
(integer) 19
127.0.0.1:6379[3]> decrby views 5
(integer) 14
127.0.0.1:6379[3]> decrby views 5
(integer) 9
###########################################################
# 字符窜范围 range
127.0.0.1:6379[3]> set key1 "hello,world"
OK
127.0.0.1:6379[3]> get key1
"hello,world"
127.0.0.1:6379[3]> getrange key1 0 3 # 截取字符串 [0,3]
"hell"
127.0.0.1:6379[3]> getrange key1 0 -1 # 获取全部的字符串 和 get key是一样的
"hello,world"
127.0.0.1:6379[3]> set key2 hello,world # 双引号加或者不加都是字符窜
OK
127.0.0.1:6379[3]> get key2
"hello,world"
#######################################################
# 替换
127.0.0.1:6379[3]> set key2 abcd
OK
127.0.0.1:6379[3]> get key2
"abcd"
127.0.0.1:6379[3]> setrange key2 1 xx # 替换指定位置 下标 开始的字符串!
(integer) 4
127.0.0.1:6379[3]> get key2
"axxd"
###################################################
# setex (set with expire) #设置过期时间
# setnx (set if not exist) # 不存在设置(在分布式锁中会常常使用!)
127.0.0.1:6379[3]> setex key3 30 "hello" # 设置key3 的值为 hello,30秒后过期
OK
127.0.0.1:6379[3]> ttl key3
(integer) 22
127.0.0.1:6379[3]> get key3
"hello"
127.0.0.1:6379[3]> ttl key3
(integer) 11
127.0.0.1:6379[3]> setnx mykey "redis" # 如果mykey 不存在,创建mykey
(integer) 1
127.0.0.1:6379[3]> get key3
(nil)
127.0.0.1:6379[3]> ttl key3
(integer) -2
127.0.0.1:6379[3]> setnx mykey "mongodb" # 如果mykey存在,创建失败!
(integer) 0
127.0.0.1:6379[3]> get mykey
"redis"
###############################################
# mset
# mget
127.0.0.1:6379[3]> mset k1 v1 k2 v2 k3 v3 # 同时设置多个值
OK
127.0.0.1:6379[3]> keys *
1) "views"
2) "sex"
3) "name2"
4) "k3"
5) "name"
6) "k2"
7) "k1"
8) "age"
9) "mykey"
10) "name1"
11) "key2"
12) "key1"
127.0.0.1:6379[3]> mget k1 k2 k3 # 同时获取多个值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379[3]> msetnx k1 v1 k4 v4 # msetnx 是一个原子性的操作,要么一起成功,要么一起 失败
(integer) 0
127.0.0.1:6379[3]> get k4
(nil)
###########################################################
# 对象设置
# 设置一个user:1 对象 值为 json字符来保存一个对象!
# 这里的key是一个巧妙的设计: user:{id}:{filed} , 如此设计在Redis中是完全OK了!
127.0.0.1:6379[3]> set user:1 {name:zhangsan,age:3}
OK
127.0.0.1:6379[3]> mset user:1:name zhangsan1 user:1:age 2
OK
127.0.0.1:6379[3]> mget user:1:name user:1:age
1) "zhangsan1"
2) "2"
###########################################################
#getset
127.0.0.1:6379[3]> getset db redis # 如果不存在值,则返回 nil,先get 没有,在set
(nil)
127.0.0.1:6379[3]> get db # 有了
"redis"
127.0.0.1:6379[3]> getset db mongodb # 如果存在值先返回原来的值,在设置新值
"redis"
127.0.0.1:6379[3]> get db # 返回的是新值
"mongodb"
List(列表)
在redis里面,我们可以把list玩成 ,栈、队列、阻塞队列!
所有的list命令都是用l开头的,Redis不区分大小命令
127.0.0.1:6379[3]> lpush list one # 将一个值或者多个值,插入到列表头部 (左)
(integer) 1
127.0.0.1:6379[3]> lpush list 2
(integer) 2
127.0.0.1:6379[3]> lpush list 3
(integer) 3
127.0.0.1:6379[3]> lrange list 0 -1 # 获取list中值!
1) "3"
2) "2"
3) "one"
127.0.0.1:6379[3]> lrange list 0 1
1) "3"
2) "2"
127.0.0.1:6379[3]> rpush list right # 将一个值或者多个值,插入到列表尾部 (右)
(integer) 4
127.0.0.1:6379[3]> lrange list 0 -1
1) "3"
2) "2"
3) "one"
4) "right"
###########################################################
# lpop
# rpop
127.0.0.1:6379[3]> lpop list
"3"
127.0.0.1:6379[3]> rpop list
"right"
127.0.0.1:6379[3]> lrange list 0 -1
1) "2"
2) "one"
###########################################################
# lindex
127.0.0.1:6379[3]> lindex list 1 # 通过下标获得 list 中的某一个值!
"one"
127.0.0.1:6379[3]> lindex list 0
"2"
###########################################################
# Llen
127.0.0.1:6379[3]> lpush list one
(integer) 3
127.0.0.1:6379[3]> lpush list two
(integer) 4
127.0.0.1:6379[3]> Lpush list 3
(integer) 5
127.0.0.1:6379[3]> llen list # 返回列表的长度
(integer) 5
###########################################################
# 一出指定的值
# 取关 uid
# Lrem
127.0.0.1:6379[3]> lrange list 0 -1
1) "3"
2) "two"
3) "one"
4) "2"
5) "one"
127.0.0.1:6379[3]> lrem list 1 two # 移除list集合中指定个数的value,精确匹配
(integer) 1
127.0.0.1:6379[3]> lrange list 0 -1
1) "3"
2) "one"
3) "2"
4) "one"
127.0.0.1:6379[3]> lrem list 1 one
(integer) 1
127.0.0.1:6379[3]> lrem list 1 one
(integer) 1
127.0.0.1:6379[3]> lrange list 0 -1
1) "3"
2) "2"
127.0.0.1:6379[3]> lpush list 3
(integer) 3
127.0.0.1:6379[3]> lrange list 0 -1
1) "3"
2) "3"
3) "2"
127.0.0.1:6379[3]> lrem list 2 3
(integer) 2
127.0.0.1:6379[3]> lrange list 0 -1
1) "2"
###########################################################
# trim 修剪
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]> lpush mylist one
(integer) 1
127.0.0.1:6379[3]> lpush mylist two
(integer) 2
127.0.0.1:6379[3]> lpush mylist three
(integer) 3
127.0.0.1:6379[3]> lpush mylist four
(integer) 4
127.0.0.1:6379[3]> lrange mylist 0 -1
1) "four"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379[3]> ltrim mylist 1 2 # 通过下标截取指定的长度,这个list已经被改变了,截断了 只剩下截取的元素
OK
127.0.0.1:6379[3]> lrange mylist 0 -1
1) "three"
2) "two"
###########################################################
rpoplpush # 移除列表的最后一个元素,将他移动到新的列表中!
127.0.0.1:6379[3]> rpush mylist "hello"
(integer) 3
127.0.0.1:6379[3]> rpush mylist "hello1"
(integer) 4
127.0.0.1:6379[3]> rpush mylist "hello2"
(integer) 5
127.0.0.1:6379[3]> lrange mylist 0 -1
1) "three"
2) "two"
3) "hello"
4) "hello1"
5) "hello2"
127.0.0.1:6379[3]> rpoplpush mylist mylist2 # 移除列表的最后一个元素,将他移动到新的 列表中!
"hello2"
127.0.0.1:6379[3]> lrange mylist 0 -1
1) "three"
2) "two"
3) "hello"
4) "hello1"
127.0.0.1:6379[3]> lrange mylist2 0 -1
1) "hello2"
###########################################################
# lset 将列表中指定下标的值替换为另一个值,更新操作
127.0.0.1:6379[3]> exists list # 判断这个列表是否存在
(integer) 0
127.0.0.1:6379[3]> lset list 0 item # 如果不存在列表我们去更新就会报错
(error) ERR no such key
127.0.0.1:6379[3]> lpush list v1
(integer) 1
127.0.0.1:6379[3]> lrange list 0 -1
1) "v1"
127.0.0.1:6379[3]> lset list 0 item # 如果存在,更新当前下标的值
OK
127.0.0.1:6379[3]> lrange list 0 -1
1) "item"
127.0.0.1:6379[3]> lset list 3 v3 # 如果不存在则报错
(error) ERR index out of range
###########################################################
# linsert # 将某个具体的value插入到列中某个元素的前面或者后面!
127.0.0.1:6379[3]> rpush mylist hello
(integer) 1
127.0.0.1:6379[3]> rpush mylist world
(integer) 2
127.0.0.1:6379[3]> linsert mylist before world other
(integer) 3
127.0.0.1:6379[3]> lrange mylist 0 -1
1) "hello"
2) "other"
3) "world"
127.0.0.1:6379[3]> linsert mylist after hello another
(integer) 4
127.0.0.1:6379[3]> lrange mylist 0 -1
1) "hello"
2) "another"
3) "other"
4) "world"
小结
- 实际上是一个链表,before Node after ,left,right 都可以插入值
- 如果key 不存在,创建新的链表
- 如果key存在,新增内容
- 如果移除了所有值,空链表,也代表不存在!
- 在两边插入或者改动值,效率最高! 中间元素,相对来说效率会低一点
- 消息排队!消息队列 (Lpush Rpop), 栈( Lpush Lpop)
Set(集合)
set中的值不能重复!
127.0.0.1:6379[3]> sadd myset hello # set集合中添加
(integer) 1
127.0.0.1:6379[3]> sadd myset world
(integer) 1
127.0.0.1:6379[3]> sadd myset mario
(integer) 1
127.0.0.1:6379[3]> smembers myset # 查看指定set的所有值
1) "mario"
2) "world"
3) "hello"
127.0.0.1:6379[3]> sadd myset one
(integer) 1
127.0.0.1:6379[3]> sadd myset two
(integer) 1
127.0.0.1:6379[3]> smembers myset # 查看指定set的所有值 无序!
1) "mario"
2) "one"
3) "world"
4) "hello"
5) "two"
127.0.0.1:6379[3]> sismember myset three # 判断某一个值是不是在set集合中!
(integer) 0
127.0.0.1:6379[3]> sismember myset one
(integer) 1
127.0.0.1:6379[3]> scard myset # 获取set集合中的内容元素个数!
(integer) 5
127.0.0.1:6379[3]> srem myset hello # 移除set集合中的指定元素
(integer) 1
127.0.0.1:6379[3]> smembers myset
1) "one"
2) "mario"
3) "two"
4) "world"
###########################################################
# set 无序不重复集合。抽随机!
127.0.0.1:6379[3]> srandmember myset # 随机抽选出一个元素
"one"
127.0.0.1:6379[3]> srandmember myset
"world"
127.0.0.1:6379[3]> srandmember myset
"mario"
127.0.0.1:6379[3]> srandmember myset
"world"
127.0.0.1:6379[3]> srandmember myset 2
1) "mario"
2) "world"
###########################################################
# 删除指定key ,随机删除key
127.0.0.1:6379[3]> spop myset
"world"
127.0.0.1:6379[3]> spop myset
"one"
127.0.0.1:6379[3]> smembers myset
1) "mario"
2) "two"
###########################################################
# 将一个指定的值移动到另外一个set集合
127.0.0.1:6379[3]> sadd myset hello
(integer) 1
127.0.0.1:6379[3]> sadd myset world
(integer) 1
127.0.0.1:6379[3]> sadd myset three
(integer) 1
127.0.0.1:6379[3]> smove myset myset2 hello
(integer) 1
127.0.0.1:6379[3]> smove myset myset2 hello00 # 不存在指定值则不移
(integer) 0
127.0.0.1:6379[3]> smembers myset
1) "mario"
2) "two"
3) "three"
4) "world"
127.0.0.1:6379[3]> smembers myset2
1) "hello"
###########################################################
# 集合 共同关注
- 并集 SUNION
- 交集 SINTER
- 差集 SDIFF
127.0.0.1:6379[3]> sadd key1 a
(integer) 1
127.0.0.1:6379[3]> sadd key1 b
(integer) 1
127.0.0.1:6379[3]> sadd key1 c
(integer) 1
127.0.0.1:6379[3]> sadd key2 c
(integer) 1
127.0.0.1:6379[3]> sadd key2 d
(integer) 1
127.0.0.1:6379[3]> sadd key2 e
(integer) 1
127.0.0.1:6379[3]> smembers key1
1) "c"
2) "b"
3) "a"
127.0.0.1:6379[3]> smembers key2
1) "e"
2) "d"
3) "c"
127.0.0.1:6379[3]> sdiff key1 key2
1) "b"
2) "a"
127.0.0.1:6379[3]> sinter key1 key2
1) "c"
127.0.0.1:6379[3]> sunion key1 key2
1) "b"
2) "c"
3) "a"
4) "e"
5) "d"
使用场景
- 微博,A用户将所有关注的人放在一个set集合中!将它的粉丝也放在一个集合中!
- 共同关注,共同爱好,二度好友,推荐好友!(六度分割理论)
Hash(哈希)
Map 集合,本质与String类型差别不大,依然是key-value
设计: hset myhash key value
127.0.0.1:6379[3]> hset myhash field1 mario # set一个具体 key-vlaue
(integer) 1
127.0.0.1:6379[3]> hget myhash field1 # 获取一个字段值
"mario"
127.0.0.1:6379[3]> hmset myhash field1 hello field2 world # set多个 key-vlaue
OK
127.0.0.1:6379[3]> hmget myhash field1 field2 # 获取多个字段的值
1) "hello"
2) "world"
127.0.0.1:6379[3]> hgetall myhash # 获取全部的数据
1) "field1"
2) "hello"
3) "field2"
4) "world"
127.0.0.1:6379[3]> hdel myhash field1 # 删除hash指定key字段!对应的value值也就消失了!
(integer) 1
127.0.0.1:6379[3]> hgetall myhash
1) "field2"
2) "world"
###########################################################
# hlen
127.0.0.1:6379[3]> hset myhash field1 hello
(integer) 1
127.0.0.1:6379[3]> hgetall myhash
1) "field2"
2) "world"
3) "field1"
4) "hello"
127.0.0.1:6379[3]> hlen myhash
(integer) 2
###########################################################
# hexists 判断是否存在
127.0.0.1:6379[3]> hexists myhash field1
(integer) 1
127.0.0.1:6379[3]> hexists myhash field3
(integer) 0
###########################################################
# hkeys 获取所有的key
# hvals 获取所有的values
127.0.0.1:6379[3]> hkeys myhash
1) "field2"
2) "field1"
127.0.0.1:6379[3]> hvals myhash
1) "world"
2) "hello"
###########################################################
# incr
# decr
127.0.0.1:6379[3]> hset myhash field3 5 #指定增量!
(integer) 1
127.0.0.1:6379[3]> hincrby myhash field3 1
(integer) 6
127.0.0.1:6379[3]> hincrby myhash field3 3
(integer) 9
127.0.0.1:6379[3]> hincrby myhash field3 -1
(integer) 8
127.0.0.1:6379[3]> hsetnx myhash field4 hello # 如果不存在则可以设置
(integer) 1
127.0.0.1:6379[3]> hsetnx myhash field4 hello2 # 如果存在则不能设置
(integer) 0
小结
- hash变更的数据 user name age,尤其是是用户信息之类的,经常变动的信息!
- hash 更适合于对象的存储,String更加适合字符串存储!
Zset(有序集合)
在set的基础上,增加了一个值,set k1 v1 zset k1 score1 v1
127.0.0.1:6379[3]> zadd myset 1 one # 添加一个值
(integer) 1
127.0.0.1:6379[3]> zrange myset 0 -1
1) "one"
127.0.0.1:6379[3]> zadd myset 2 two 3 three # 添加多个值
(integer) 2
127.0.0.1:6379[3]> zrange myset 0 -1
1) "one"
2) "two"
3) "three"
###########################################################
# 排序
# ZRANGEBYSCORE key min max
127.0.0.1:6379[3]> zadd salary 2500 xioaming # 添加三个用户
(integer) 1
127.0.0.1:6379[3]> zadd salary 5000 xiaohong
(integer) 1
127.0.0.1:6379[3]> zadd salary 10000 xiaosan
(integer) 1
127.0.0.1:6379[3]> zrangebyscore salary -inf +inf # 显示全部的用户 从小到大!
1) "xioaming"
2) "xiaohong"
3) "xiaosan"
127.0.0.1:6379[3]> zrevrange salary 0 -1 # 从大到进行排序!
1) "xiaosan"
2) "xiaohong"
3) "xioaming"
127.0.0.1:6379[3]> zrangebyscore salary -inf +inf withscores # 显示全部的用户并且附带salary
1) "xioaming"
2) "2500"
3) "xiaohong"
4) "5000"
5) "xiaosan"
6) "10000"
127.0.0.1:6379[3]> zrangebyscore salary -inf 2500 withscores # 显示工资小于2500员工的升 序排序!
1) "xioaming"
2) "2500"
127.0.0.1:6379[3]> zrangebyscore salary 3500 +inf withscores # 显示工资大于3500员工的升 序排序!
1) "xiaohong"
2) "5000"
3) "xiaosan"
4) "10000"
###########################################################
# zrem 移除元素
127.0.0.1:6379[3]> zrange salary 0 -1
1) "xioaming"
2) "xiaohong"
3) "xiaosan"
127.0.0.1:6379[3]> zrem salary xioaming # 移除有序集合中的指定元素
(integer) 1
127.0.0.1:6379[3]> zrange salary 0 -1
1) "xiaohong"
2) "xiaosan"
127.0.0.1:6379[3]> zcard salary # 获取有序集合中的个数
(integer) 2
###########################################################
# zcount key min max
# 分数值在 min 和 max 之间的成员的数量
127.0.0.1:6379> ZADD myzset 1 "hello"
(integer) 1
127.0.0.1:6379> ZADD myzset 1 "foo"
(integer) 1
127.0.0.1:6379> ZADD myzset 2 "world" 3 "bar"
(integer) 2
127.0.0.1:6379> ZCOUNT myzset 1 3 # 获取指定区间的成员数量!
(integer) 4
使用场景
- set 排序 存储班级成绩表,工资表排序!
- 普通消息,1, 重要消息 2,带权重进行判断
- 排行榜应用实现,取Top N 测试
三种特殊数据类型
Geospatial 地理位置
Redis GEO 主要用于存储地理位置信息,并对存储的信息进行操作,该功能在 Redis 3.2 版本新增。
场景分析: 朋友的定位,附近的人,打车距离计算? 这个功能可以推算地理位置的信息,两地之间的距离,方圆几里的人!
Redis GEO 操作方法有:
- geoadd:添加地理位置的坐标。
- geopos:获取地理位置的坐标。
- geodist:计算两个位置之间的距离。
- georadius:根据用户给定的经纬度坐标来获取指定范围内的地理位置集合。
- georadiusbymember:根据储存在位置集合里面的某个地点获取指定范围内的地理位置集合。
- geohash:返回一个或多个位置对象的 geohash 值。
# getadd 添加地理位置
# 参数 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 chongqi 114.05 22.52 shengzhen
(integer) 2
127.0.0.1:6379> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2
###########################################################
# getpos 获得当前定位:一定是一个坐标值!
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 chongqi
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
###########################################################
# GEODIST
单位:
- m 表示单位为米。
- km 表示单位为千米。
- mi 表示单位为英里。
- ft 表示单位为英尺。
127.0.0.1:6379> geodist china:city beijing shanghai km # 查看上海到北京的直线距离
"1067.3788"
127.0.0.1:6379> geodist china:city beijing chongqi km
"1464.0708"
###########################################################
# georadius 以给定的经纬度为中心, 找出某一半径内的元素
# 以110,30 这个经纬度为中心,寻 找方圆1000km内的城市
127.0.0.1:6379> georadius china:city 110 30 1000 km
1) "chongqi"
2) "shengzhen"
# WITHDIST: 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回
127.0.0.1:6379> georadius china:city 110 30 500 km withdist # 显示到500km距离的位置
1) 1) "chongqi"
2) "341.9374"
127.0.0.1:6379> georadius china:city 110 30 500 km
1) "chongqi"
# WITHCOORD: 将位置元素的经度和维度也一并返回
127.0.0.1:6379> georadius china:city 110 30 500 km withcoord
1) 1) "chongqi"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
# COUNT 限定返回的记录数。
127.0.0.1:6379> georadius china:city 110 30 500 km withdist withcoord count 1
1) 1) "chongqi"
2) "341.9374"
3) 1) "106.49999767541885376"
2) "29.52999957900659211"
###########################################################
# 找出位于指定元素周围的其他元素!
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1000 km
1) "beijing"
2) "xian"
###########################################################
# GEOHASH 命令 - 返回一个或多个位置元素的 Geohash 表示
127.0.0.1:6379> geohash china:city beijing chongqi
1) "wx4fbxxfke0"
2) "wm5xzrybty0"
###########################################################
# GEO 底层的实现原理其实就是 Zset!我们可以使用Zset命令来操作geo
127.0.0.1:6379> zrange china:city 0 -1
1) "chongqi"
2) "shengzhen"
3) "shanghai"
4) "beijing"
127.0.0.1:6379> zrem china:city beijing
(integer) 1
127.0.0.1:6379> zrange china:city 0 -1
1) "chongqi"
2) "shengzhen"
3) "shanghai"
Hyperloglog
什么是基数?
比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。
127.0.0.1:6379> pfadd mykey a b c d e f j i j k l ## 创建第一组元素 mykey
(integer) 1
127.0.0.1:6379> pfcount mykey # 统计 mykey 元素的基数数量
(integer) 10
127.0.0.1:6379> pfadd mykey2 i j d e r t
(integer) 1
127.0.0.1:6379> pfcount mykey2
(integer) 6
127.0.0.1:6379> pfmerge mykey3 mykey mykey2 # 合并两组 mykey mykey2 => mykey3 并集
OK
127.0.0.1:6379> pfcount mykey3 # 看并集的数量!
(integer) 12
Bitmap 位存储
统计用户信息,活跃,不活跃! 登录 、 未登录! 打卡,365打卡! 两个状态的,都可以使用Bitmaps!
Bitmap 位图,数据结构! 都是操作二进制位来进行记录,就只有0 和 1 两个状态!
使用bitmap 来记录 周一到周日的打卡!
周一:1 周二:0 周三:0 周四:1 …
127.0.0.1:6379> setbit sign 0 1
(integer) 0
127.0.0.1:6379> setbit sign 1 0
(integer) 0
127.0.0.1:6379> setbit sign 2 1
(integer) 0
127.0.0.1:6379> setbit sign 3 0
(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 0
(integer) 0
127.0.0.1:6379> getbit sign 3 # 查看某一天是否有打卡!
(integer) 0
127.0.0.1:6379> getbit sign 6
(integer) 0
127.0.0.1:6379> bitcount sign # 统计操作,统计 打卡的天数!
(integer) 4
Redis 发布订阅
Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。
Redis 客户端可以订阅任意数量的频道。
下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:
当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:
实例
以下实例演示了发布订阅是如何工作的,需要开启两个 redis-cli 客户端。
在我们实例中我们创建了订阅频道名为 runoobChat:
127.0.0.1:6379> subscribe runoobChat
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "runoobChat"
3) (integer) 1
现在,我们先重新开启个 redis 客户端,然后在同一个频道 runoobChat 发布两次消息,订阅者就能接收到消息。
mario@mario:/mnt/c/Users/admin$ redis-cli
127.0.0.1:6379> publish runoobChat "redis publish test"
(integer) 1
127.0.0.1:6379> publish runoobChat "learn redis by runoob.com"
(integer) 1
订阅者的客户端会显示如下消息
127.0.0.1:6379> subscribe runoobChat
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "runoobChat"
3) (integer) 1
1) "message"
2) "runoobChat"
3) "redis publish test"
1) "message"
2) "runoobChat"
3) "learn redis by runoob.com"
Redis 发布订阅命令
下表列出了 redis 发布订阅常用命令:
序号 | 命令 | 描述 |
1 | PSUBSCRIBE pattern [pattern …] | 订阅一个或多个符合给定模式的频道。 |
2 | PUBSUB subcommand [argument [argument …]] | 查看订阅与发布系统状态 |
3 | PUBLISH channel message | 将信息发送到指定的频道 |
4 | PUNSUBSCRIBE [pattern [pattern …]] | 退订所有给定模式的频道 |
5 | SUBSCRIBE channel [channel …] | 订阅给定的一个或多个频道的信息 |
6 | UNSUBSCRIBE [channel [channel …]] | 指退订给定的频道 |
事务
Redis 事务本质:一组命令的集合! 一个事务中的所有命令都会被序列化,在事务执行过程的中,会按照顺序执行!
一次性、顺序性、排他性!执行一些列的命令!
– 队列 set set set 执行–
- Redis事务没有没有隔离级别的概念!
- 所有的命令在事务中,并没有直接被执行!只有发起执行命令的时候才会执行!Exec
- Redis单条命令式保存原子性的,但是事务不保证原子性!
redis的事务:
- 开启事务(multi)
- 命令入队(…)
- 执行事务(exec)
mario@mario:/mnt/c/Users/admin$ redis-cli
127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379> set k1 v1 # 命令入队
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k3
QUEUED
127.0.0.1:6379> exec # 执行事务
1) OK
2) OK
3) OK
4) "v3"
###########################################################
# 取消事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> discard # 取消事务
OK
127.0.0.1:6379> get k4
(nil)
###########################################################
# 编译型异常(代码有问题! 命令有错!) ,事务中所有的命令都不会被执行!
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k2 v22
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> getset k3
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k6 v6
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k2
"v2"
###########################################################
# 运行时异常(1/0), 如果事务队列中存在语法性,那么执行命令的时候,其他命令是可以正常执行的,错误命令抛出异常!
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr k1 # 会执行的时候失败!
QUEUED
127.0.0.1:6379> set k2 v222
QUEUED
127.0.0.1:6379> set k3 v333
QUEUED
127.0.0.1:6379> get k3
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range # 第一条报错,后续正常
2) OK
3) OK
4) "v333"
127.0.0.1:6379> get k2
"v222"
监控
悲观锁:很悲观,认为什么时候都会出问题,无论做什么都会加锁!
WATCH key [key …]:监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断.
UNWATCH:取消 WATCH 命令对所有 key 的监视。
乐观锁:
- 很乐观,认为什么时候都不会出问题,所以不会上锁! 更新数据的时候去判断一下,在此期间是否有人修改过这个数据,
- 获取version
- 更新的时候比较
Redis测监视测试
# 正常执行
127.0.0.1:6379[3]> set money 100
OK
127.0.0.1:6379[3]> set out 0
OK
127.0.0.1:6379[3]> watch money
OK
127.0.0.1:6379[3]> multi
OK
127.0.0.1:6379[3]> decrby money 20
QUEUED
127.0.0.1:6379[3]> incrby out 20
QUEUED
127.0.0.1:6379[3]> exec
1) (integer) 80
2) (integer) 20
###########################################################
# 测试多线程修改值 , 使用watch 可以当做redis的乐观锁操作!
127.0.0.1:6379[3]> watch money
OK
127.0.0.1:6379[3]> multi
OK
127.0.0.1:6379[3]> decrby money 10
QUEUED
127.0.0.1:6379[3]> incrby out 10
QUEUED
# 此时,在另外一个线程中修改money 的值
mario@mario:/mnt/c/Users/admin$ redis-cli
127.0.0.1:6379> select 3
OK
127.0.0.1:6379[3]> keys *
1) "money"
2) "out"
127.0.0.1:6379[3]> incrby money 29
(integer) 99
# 继续执行上一个进程中exec操作:
127.0.0.1:6379[3]> exec # 执行失败
(nil)
# 如果修改失败,获取最新的值就好
127.0.0.1:6379[3]> unwatch
OK
127.0.0.1:6379[3]> watch money
OK
127.0.0.1:6379[3]> multi
OK
127.0.0.1:6379[3]> decrby money 10
QUEUED
127.0.0.1:6379[3]> incrby out 10
QUEUED
127.0.0.1:6379[3]> exec
1) (integer) 89
2) (integer) 40