redis 默认有 16 个数据库
默认使用的是第 0 个
可以使用 select 进行切换数据库,dbsize 查看当前数据库大小
127.0.0.1:6379> select 3 #切换数据库 | |
OK | |
127.0.0.1:6379[3]> dbsize #查看当前 DB 大小 | |
(integer) 0 | |
127.0.0.1:6379[3]> |
不同的数据库可以存储不同的值 (不互通) 1 中 不能访问到 2 中的数据
127.0.0.1:6379> select 3 | |
OK | |
127.0.0.1:6379[3]> dbsize | |
(integer) 0 | |
127.0.0.1:6379[3]> set name dkx | |
OK | |
127.0.0.1:6379[3]> dbsize | |
(integer) 1 | |
127.0.0.1:6379[3]> select 0 | |
OK | |
127.0.0.1:6379> get name | |
(nil) | |
127.0.0.1:6379> select 3 | |
OK | |
127.0.0.1:6379[3]> get name | |
"dkx" | |
127.0.0.1:6379[3]> |
查看当前数据库所有的 key: keys *
127.0.0.1:6379> keys * #这个库是空的下面的 key 都是测试工具测试的时候添加的 | |
1) "mylist" | |
2) "key:__rand_int__" | |
3) "counter:__rand_int__" | |
127.0.0.1:6379> select 3 | |
OK | |
127.0.0.1:6379[3]> keys * | |
1) "user" | |
127.0.0.1:6379[3]> |
清空当前数据库: flushdb
(在数据库 3 中使用 flushdb, 数据库 3 中数据全被清除,数据库 0 中的数据不受影响)
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]> |
接上命令我们在 3 号数据库中有数据我们切换到 0 使用命令: flushall
(清空所有数据库)
127.0.0.1:6379[3]> select 0 | |
OK | |
127.0.0.1:6379> flushall | |
OK | |
127.0.0.1:6379> select 3 | |
OK | |
127.0.0.1:6379[3]> keys * | |
(empty list or set) | |
127.0.0.1:6379[3]> |
判断指定 key 是否存在: exists
127.0.0.1:6379> exists user | |
(integer) 0 | |
127.0.0.1:6379> set user lisi | |
OK | |
127.0.0.1:6379> exists user | |
(integer) 1 | |
127.0.0.1:6379> |
设置 key 的过期时间: expire
(翻译 expire : 到期) 过期后指定的 key 就会被删除
查看 key 的有效期剩余秒数: ttl
127.0.0.1:6379> expire name 10 | |
(integer) 1 | |
127.0.0.1:6379> ttl name | |
(integer) 6 | |
127.0.0.1:6379> get name | |
"lisi" | |
127.0.0.1:6379> ttl name | |
(integer) -2 | |
127.0.0.1:6379> get name | |
(nil) | |
127.0.0.1:6379> |
移动当前的 key 到指定数据库: move
127.0.0.1:6379> set name a | |
OK | |
127.0.0.1:6379> get name | |
"a" | |
127.0.0.1:6379> move name 3 | |
(integer) 1 | |
127.0.0.1:6379> get name | |
(nil) | |
127.0.0.1:6379> select 3 | |
OK | |
127.0.0.1:6379[3]> get name | |
"a" | |
127.0.0.1:6379[3]> |
查看当前 key 的数据类型: type
(查看 key 不存在返回 "none")
127.0.0.1:6379> get name | |
"liusang" | |
127.0.0.1:6379> type name | |
string | |
127.0.0.1:6379> |
<font style="color:red">incr,decr,incrby,decrby 只能用于 String 类型的 key</font>
将数值加 1: incr
(直接使用可以创建一个 String 类型的 key 比如执行: incr ar 这里的 ar 并不存在,创建一个 1 的 number)
将数值减 1: decr
(直接使用可以创建一个 String 类型的 key 比如执行: decr ar 这里的 ar 并不存在,创建一个 - 1 的 number)
127.0.0.1:6379> set number 0 | |
OK | |
127.0.0.1:6379> get number | |
"0" | |
127.0.0.1:6379> incr number | |
(integer) 1 | |
127.0.0.1:6379> get number | |
"1" | |
127.0.0.1:6379> decr number | |
(integer) 0 | |
127.0.0.1:6379> get number | |
"0" | |
127.0.0.1:6379> |
将数值增加指定数量: incrby
将数值减少指定数量: decrby
127.0.0.1:6379> get number | |
"0" | |
127.0.0.1:6379> incrby number 10 | |
(integer) 10 | |
127.0.0.1:6379> get number | |
"10" | |
127.0.0.1:6379> decrby number 11 | |
(integer) -1 | |
127.0.0.1:6379> get number | |
"-1" | |
127.0.0.1:6379> |
redis 是单线程的!
明白 Redis 是很快的,官方表示,Redis 是基于内存操作,CPU 不是 Redis 性能瓶颈,Redis 的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程来实现,就使用单线程了!所以就使用了单线程了
Redis 是 C 语言写的,官方提供的数据为 100000 + 的 QPS, 说明完全不比同样是使用 key-value 的 Memecache 差!
Redis 为什么单线程还这么快?
1. 误区 1: 高性能的服务器一定是多线程的?
2. 误区 2: 多线程 (CPU 上下文切换) 一定比单线程效率高
CPU>内存>硬盘
的速度
核心:redis 是将所有的数据全部放在内存中,所以说使用单线程去操作效率就是最高的,多线程 (CPU 上下文切换: 耗时操作!!!), 对于内存系统来说,如果没有上下文切换效率就是最高的!多次读写都是在一个 CPU 上的,在内存情况下,这个就是最佳方案!
# 五大数据类型
# String (字符串)
获取字符串的字符长度 strlen
127.0.0.1:6379> set user "liusang" | |
OK | |
127.0.0.1:6379> strlen user | |
(integer) 7 | |
127.0.0.1:6379> |
追加字符串 (如果当前 key 不存在那么就创建): append key value
(只能对 String 类型使用)
127.0.0.1:6379> get name | |
"liusang" | |
127.0.0.1:6379> append name "shiwonanshen!" | |
(integer) 20 | |
127.0.0.1:6379> get name | |
"liusangshiwonanshen!" | |
127.0.0.1:6379> |
截取指定区间的字符串范围 (start:0 end:-1 表示全部): getrange
127.0.0.1:6379> set name liusangshiwonanshen | |
OK | |
127.0.0.1:6379> get name | |
"liusangshiwonanshen" | |
127.0.0.1:6379> getrange name 0 4 | |
"liusa" | |
127.0.0.1:6379> getrange name 0 -1 | |
"liusangshiwonanshen" | |
127.0.0.1:6379> |
替换指定位置开始的字符串: setrange
127.0.0.1:6379> get name | |
"liusangshiwonanshen" | |
127.0.0.1:6379> setrange name 2 **** | |
(integer) 19 | |
127.0.0.1:6379> get name | |
"li****gshiwonanshen" | |
127.0.0.1:6379> |
设置过期时间: setex
127.0.0.1:6379> setex name 10 "hello"#设置hello10秒后过期 | |
OK | |
127.0.0.1:6379> ttl name | |
(integer) 3 | |
127.0.0.1:6379> get name | |
"hello" | |
127.0.0.1:6379> ttl name | |
(integer) -2 | |
127.0.0.1:6379> get name | |
(nil) | |
127.0.0.1:6379> |
不存在则创建,存在则不创建 (在分布式锁中会常常使用): setnx
127.0.0.1:6379> setnx myname zhangsan #如果 myname 不存在则创建 | |
(integer) 1 | |
127.0.0.1:6379> setnx myname zhangsan #myname 存在则创建失败 | |
(integer) 0 | |
127.0.0.1:6379> get myname | |
"zhangsan" | |
127.0.0.1:6379> |
同时设置多个值: mset
同时获取多个值: mget
(不是只能同时获取 mset 创建的 key 其它的是 String 类型就可以)
127.0.0.1:6379> keys * | |
(empty list or set) | |
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 #同时设置多个值 | |
OK | |
127.0.0.1:6379> mget k1 k2 k3 #同时获取多个值 | |
1) "v1" | |
2) "v2" | |
3) "v3" | |
127.0.0.1:6379> msetnx k1 v1 k4 v4 #配合 setnx 如果有存在的 key 则条件不成立 | |
(integer) 0 | |
127.0.0.1:6379> mget k4 #没有创建成功 | |
1) (nil) | |
127.0.0.1:6379> |
# 对象
127.0.0.1:6379> set user:1 {name:lisi,age:18} #设置一个 user:1 对象 值为 json 字符串来保存一个对象 | |
OK | |
127.0.0.1:6379> get user:1 | |
"{name:lisi,age:18}" | |
#这里的 key 是一个巧妙的设计: user:{id}:{filed} , 如此设计在 Redis 中是完全 OK 了 | |
127.0.0.1:6379> mset user:1:name lisi user:1:age 1 | |
OK | |
127.0.0.1:6379> mget user:1:name user:1:age | |
1) "lisi" | |
2) "1" | |
127.0.0.1:6379> |
先获取再设置值: getset
127.0.0.1:6379> getset dkx niubi | |
(nil) | |
127.0.0.1:6379> get dkx | |
"niubi" | |
127.0.0.1:6379> getset dkx 666 | |
"niubi" | |
127.0.0.1:6379> get dkx | |
"666" | |
127.0.0.1:6379> |
数据结构是相通的!
String 类似的使用场景: value 除了字符串还可以是数字!
# List
在 redis 中,我们可以把 list 玩成,栈,队列,阻塞队列
跟 String 语法类似,所有的 list 命令都是用 l , 字母 l 开头的,<font style="color:green"> 添加 :lpush 前,rpush 后 </font> | <font style="color:red"> 移除并返回 /lpop 前 /rpop 后 </font>
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> rpush list c | |
(integer) 3 | |
127.0.0.1:6379> lpop list | |
"b" | |
127.0.0.1:6379> rpop list | |
"c" | |
127.0.0.1:6379> lpop list | |
"a" | |
127.0.0.1:6379> |
通过索引获取值: lindex
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> lindex list 2 | |
"a" | |
127.0.0.1:6379> |
获取长度: llen
127.0.0.1:6379> lpush name a | |
(integer) 1 | |
127.0.0.1:6379> llen name | |
(integer) 1 | |
127.0.0.1:6379> |
移除列表中的 key: lrem key
指定范围获取列表中的值: lrange start end
127.0.0.1:6379> lpush name a | |
(integer) 1 | |
127.0.0.1:6379> lpush name b | |
(integer) 2 | |
127.0.0.1:6379> lpush name a | |
(integer) 3 | |
127.0.0.1:6379> lrange name 0 -1 | |
1) "a" | |
2) "b" | |
3) "a" | |
127.0.0.1:6379> lrem name 2 a | |
(integer) 2 | |
127.0.0.1:6379> lrange name 0 -1 | |
1) "b" |
截断,保留列表中截断的值: ltrim key start end
127.0.0.1:6379> lpush name a | |
(integer) 1 | |
127.0.0.1:6379> lpush name b | |
(integer) 2 | |
127.0.0.1:6379> lpush name c | |
(integer) 3 | |
127.0.0.1:6379> lpush name d | |
(integer) 4 | |
127.0.0.1:6379> ltrim name 1 2 #通过下标截取指定的长度,这个 list 已经被改变了,截断了只剩下截取的元素! | |
OK | |
127.0.0.1:6379> lrange name 0 -1 #查看全部元素 | |
1) "c" | |
2) "b" | |
127.0.0.1:6379> |
移除列表的最后一个元素,将它移动到新的列表中,如果移动到列表不存在则创建: rpoplpush key source distination
翻译: source (根源) , distination (目的地)
- <font style="color:red"> 只有: rpoplpush 格式没有 lpoprpush 或者 lpoplpush 的格式 </font>
127.0.0.1:6379> lrange name 0 -1 | |
1) "c" | |
2) "b" | |
127.0.0.1:6379> rpoplpush name nametwo # 移除并移动到指定列表中指定列表不存在则创建 | |
"b" | |
127.0.0.1:6379> lrange name 0 -1 # 查看原来的列表 | |
1) "c" | |
127.0.0.1:6379> lrange nametwo 0 -1 # 查看新的列表 | |
1) "b" | |
127.0.0.1:6379> |
将列表中指定存在的下标的值替换为另外一个值. 更新操作: lset key index value
127.0.0.1:6379> exists list # 判断列表是否存在 | |
(integer) 0 | |
127.0.0.1:6379> lset list 0 a # 视图向不存在的列表中添加值 | |
(error) ERR no such key | |
127.0.0.1:6379> lpush name a # 向 list 中添加值 | |
(integer) 1 | |
127.0.0.1:6379> lrange name 0 -1 # 查看列表的值 | |
1) "a" | |
127.0.0.1:6379> lset name 0 liusang # 通过 lset 指定下标替换值 | |
OK | |
127.0.0.1:6379> lrange name 0 -1 # 查看列表的值 | |
1) "liusang" | |
127.0.0.1:6379> lset name 1 lisi # 视图在存在的列表中设置不存在的索引值 | |
(error) ERR index out of range | |
127.0.0.1:6379> |
将给定 value 插入到指定 value 的 before (之前) 或者 after (之后): linsert key [before|after] pivot value
翻译: (pivot : 支点)
127.0.0.1:6379> lrange name 0 -1 | |
1) "a" | |
127.0.0.1:6379> linsert name before a niubi #将 niubi 插入到 a 的之前 | |
(integer) 2 | |
127.0.0.1:6379> lrange name 0 -1 | |
1) "niubi" | |
2) "a" | |
127.0.0.1:6379> linsert name after a 666 #将 666 插入到 a 的之后 | |
(integer) 3 | |
127.0.0.1:6379> lrange name 0 -1 | |
1) "niubi" | |
2) "a" | |
3) "666" | |
127.0.0.1:6379> |
小结
- 它实际上是一个链表,before Node after , left, right (前后,左右) 都可以插入值
- 如果 key 不存在,插入的话就会创建新的列表
- 如果 key 存在,插入的话就会新增内容
- 如果移除了所有值,空列表,也代表不存在!
- 在 <u> 两边插入或者改动值,效率最高!中间元素,相对来说效率会低 </u> 一点
<u> 消息队列 (lpush rpop), 栈 (lpush lpop)</u>
# Set (集合):set 中的值是不能重复的
单词: member (成员)
向集合中添加元素: sadd key value
查看集合中所有元素: smembers key
查看集合中是否包含该元素: sismember key value
127.0.0.1:6379> sadd myset liusang #向集合中添加元素 | |
(integer) 1 | |
127.0.0.1:6379> sadd myset niubi | |
(integer) 1 | |
127.0.0.1:6379> smembers myset #查看集合中所有元素 | |
1) "niubi" | |
2) "liusang" | |
127.0.0.1:6379> sismember myset niubi #查看集合中是否包含该元素 | |
(integer) 1 | |
127.0.0.1:6379> sismember myset 123 | |
(integer) 0 | |
127.0.0.1:6379> |
查看集合中元素个数: scard key
127.0.0.1:6379> smembers myset | |
1) "niubi" | |
2) "liusang" | |
127.0.0.1:6379> scard myset | |
(integer) 2 | |
127.0.0.1:6379> |
移除指定元素: srem key value
127.0.0.1:6379> smembers myset | |
1) "bbb" | |
2) "aaa" | |
3) "liusang" | |
127.0.0.1:6379> srem myset aaa | |
(integer) 1 | |
127.0.0.1:6379> smembers myset | |
1) "bbb" | |
2) "liusang" | |
127.0.0.1:6379> |
随机抽取指定 count 个元素: srandmember key count
(默认只抽取一个出来)
127.0.0.1:6379> smembers myset | |
1) "aaa" | |
2) "ccc" | |
3) "bbb" | |
4) "liusang" | |
127.0.0.1:6379> srandmember myset | |
"ccc" | |
127.0.0.1:6379> srandmember myset | |
"ccc" | |
127.0.0.1:6379> srandmember myset | |
"liusang" | |
127.0.0.1:6379> srandmember myset | |
"ccc" | |
127.0.0.1:6379> srandmember myset | |
"liusang" | |
127.0.0.1:6379> srandmember myset | |
"ccc" | |
127.0.0.1:6379> srandmember myset | |
"liusang" | |
127.0.0.1:6379> srandmember myset | |
"ccc" | |
127.0.0.1:6379> srandmember myset | |
"liusang" | |
127.0.0.1:6379> srandmember myset | |
"liusang" | |
127.0.0.1:6379> srandmember myset | |
"aaa" | |
127.0.0.1:6379> srandmember myset | |
"ccc" | |
127.0.0.1:6379> srandmember myset 2 | |
1) "aaa" | |
2) "liusang" | |
127.0.0.1:6379> srandmember myset 2 | |
1) "bbb" | |
2) "ccc" | |
127.0.0.1:6379> |
随机弹出元素: spop key
(弹出列表后相当于从列表中删除)
127.0.0.1:6379> smembers myset | |
1) "aaa" | |
2) "ccc" | |
3) "bbb" | |
4) "liusang" | |
127.0.0.1:6379> spop myset | |
"liusang" | |
127.0.0.1:6379> spop myset | |
"aaa" | |
127.0.0.1:6379> spop myset | |
"bbb" | |
127.0.0.1:6379> smembers myset | |
1) "ccc" | |
127.0.0.1:6379> |
将指定的元素移动到另外指定的数据库集合中: smove source distination member
(指定的数据库不存在则创建)
127.0.0.1:6379> smembers myset | |
1) "bbb" | |
2) "aaa" | |
3) "ccc" | |
127.0.0.1:6379> smove myset myset1 aaa | |
(integer) 1 | |
127.0.0.1:6379> smembers myset | |
1) "bbb" | |
2) "ccc" | |
127.0.0.1:6379> smembers myset1 | |
1) "aaa" | |
127.0.0.1:6379> keys * | |
1) "myset" | |
2) "myset1" | |
127.0.0.1:6379> |
差集: sdiff
: 假设有集合 A 和 B, 所有属于 A 且不属于 B 的元素的集合被称为 A 与 B 的差集
- <u> 示例:对于集合 A={a,<font style="color:red"> b</font>, <font style="color:red">c</font>, d} 和集合 B={<font style="color:red">b</font>, <font style="color:red">c</font>, w}, 则 A 与 B 的差集为 {a, d}</u>
交集: sinter
: 两个集合之间相同的元素
- <u> 示例:对于集合 A={a,<font style="color:red"> b</font>, <font style="color:red">c</font>, d} 和集合 B={<font style="color:red">b</font>, <font style="color:red">c</font>, w}, 则 A 与 B 的交集为 {b,c}</u>
并集: sunion
: 合并两个集合中去除相同元素 (<u> 并不是说删除,而是不算重复的保留一个 </u>) 后的整合
- <u> 示例:对于集合 A={a,<font style="color:red"> b</font>, <font style="color:red">c</font>, d} 和集合 B={<font style="color:red">b</font>, <font style="color:red">c</font>, w}, 则 A 与 B 的交集为 {a,b,c,d,w}</u>
127.0.0.1:6379> sadd k1 a | |
(integer) 1 | |
127.0.0.1:6379> sadd k1 b | |
(integer) 1 | |
127.0.0.1:6379> sadd k1 c | |
(integer) 1 | |
127.0.0.1:6379> sadd k2 c | |
(integer) 1 | |
127.0.0.1:6379> sadd k2 d | |
(integer) 1 | |
127.0.0.1:6379> sadd k2 e | |
(integer) 1 | |
127.0.0.1:6379> sdiff k1 k2 | |
1) "b" | |
2) "a" | |
127.0.0.1:6379> sinter k1 k2 | |
1) "c" | |
127.0.0.1:6379> sunion k1 k2 | |
1) "b" | |
2) "a" | |
3) "c" | |
4) "e" | |
5) "d" | |
127.0.0.1:6379> |
# Hash (哈希集合)
Map 集合,key-Map 集合,值是 map 集合!本质和 String 类型没有太大区别,还是一个简单的 key-value 而,value 是可以由两个值组成
向集合中添加一个字段值对: hset key field value field1 value1 ...
通过键字段获取值: hget key field
向集合中添加一个键多个字段值: hmset key field value field1 value1 ...
(默认的 hset 也可以添加多个)
获取集合中多个值的键的值: hmget key field field1 ...
获取全部数据: hgetall key
删除指定键中指定的字段: hdel key field field1 field2 ...
127.0.0.1:6379> hset h1 k1 niubi | |
(integer) 1 | |
127.0.0.1:6379> hget h1 k1 | |
"niubi" | |
127.0.0.1:6379> hmset h1 k1 hello k2 666 | |
OK | |
127.0.0.1:6379> hmget h1 k1 k2 | |
1) "hello" | |
2) "666" | |
127.0.0.1:6379> hgetall h1 | |
1) "k1" | |
2) "hello" | |
3) "k2" | |
4) "666" | |
127.0.0.1:6379> hdel h1 k1 | |
(integer) 1 | |
127.0.0.1:6379> hgetall h1 | |
1) "k2" | |
2) "666" | |
127.0.0.1:6379> |
获取元素个数,一对 field value 为一个个数: hlen key
127.0.0.1:6379> hgetall h1 | |
1) "k2" | |
2) "666" | |
3) "k3" | |
4) "niubi666" | |
127.0.0.1:6379> hlen h1 | |
(integer) 2 | |
127.0.0.1:6379> |
判断集合指定的键中是否存在该字段: hexists key field
127.0.0.1:6379> hexists h1 k1 | |
(integer) 0 | |
127.0.0.1:6379> hexists h1 k2 | |
(integer) 1 | |
127.0.0.1:6379> keys * | |
1) "h1" | |
127.0.0.1:6379> hgetall h1 | |
1) "k2" | |
2) "666" | |
3) "k3" | |
4) "niubi666" | |
127.0.0.1:6379> |
获取指定键中所有的 field: hkeys key
获取指定键中所有的 field 的 value: hvals key
127.0.0.1:6379> hgetall h1 | |
1) "k2" | |
2) "666" | |
3) "k3" | |
4) "niubi666" | |
127.0.0.1:6379> hkeys h1 | |
1) "k2" | |
2) "k3" | |
127.0.0.1:6379> hvals h1 | |
1) "666" | |
2) "niubi666" | |
127.0.0.1:6379> |
指定增加数量 (如果 count 给定为负数那么就是减): hincrby key field count
(如果 key 不存在则创建)
判断如果不存在字段则添加,如果存在则不添加: hsetnx key field value
127.0.0.1:6379> hset h1 k1 5 | |
(integer) 1 | |
127.0.0.1:6379> hincrby h1 k1 1 | |
(integer) 6 | |
127.0.0.1:6379> hincrby h1 k1 -1 | |
(integer) 5 | |
127.0.0.1:6379> hsetnx h1 k1 hello | |
(integer) 0 | |
127.0.0.1:6379> hget h1 k1 | |
"5" | |
127.0.0.1:6379> hsetnx h1 k2 hello | |
(integer) 1 | |
127.0.0.1:6379> hget h1 k2 | |
"hello" | |
127.0.0.1:6379> |
hash 变更的数据 user, name, age, 尤其是用户信息之类的,经常变动的信息!hash 更适合于对象的存储,String 更适合字符串存储
# Zset (有序集合)
向集合中添加元素: zadd key NX|XX
前者按编号排序,后者存储 value
按照指定范围获取集合中的元素: zrange key start end
127.0.0.1:6379> zadd h1 1 a | |
(integer) 1 | |
127.0.0.1:6379> zadd h1 2 b | |
(integer) 1 | |
127.0.0.1:6379> zadd h1 3 e | |
(integer) 1 | |
127.0.0.1:6379> zadd h1 4 c | |
(integer) 1 | |
127.0.0.1:6379> zrange h1 0 -1 | |
1) "a" | |
2) "b" | |
3) "e" | |
4) "c" | |
127.0.0.1:6379> |
查询指定的键从最小值到最大值排序获取 (-inf 不能大于 + inf 否则报错 (+inf 可指定条件)): zrangebyscore
127.0.0.1:6379> zadd h1 5000 xiaogong #添加三个用户分别有工资 | |
(integer) 1 | |
127.0.0.1:6379> zadd h1 3500 dagong | |
(integer) 1 | |
127.0.0.1:6379> zadd h1 2500 liuhong | |
(integer) 1 | |
127.0.0.1:6379> zadd h1 500 dazuo | |
(integer) 1 | |
127.0.0.1:6379> zrange h1 0 -1 #查询所有的用户,默认排序按降序 | |
1) "dazuo" | |
2) "liuhong" | |
3) "dagong" | |
4) "xiaogong" | |
127.0.0.1:6379> zrangebyscore h1 -inf +inf #排序查询 从负∞ 到正∞ | |
1) "dazuo" | |
2) "liuhong" | |
3) "dagong" | |
4) "xiaogong" | |
127.0.0.1:6379> zrangebyscore h1 -inf 2500 #查询 从负∞ 到指定的 2500 的工资为止 | |
1) "dazuo" | |
2) "liuhong" | |
127.0.0.1:6379> |
通过键以升序获取元素 (反转查询结果): zrevrange key start end
127.0.0.1:6379> zrangebyscore h1 -inf +inf withscores | |
1) "dazuo" | |
2) "500" | |
3) "liuhong" | |
4) "2500" | |
5) "xiaogong" | |
6) "5000" | |
127.0.0.1:6379> zrevrange h1 0 -1 | |
1) "xiaogong" | |
2) "liuhong" | |
3) "dazuo" | |
127.0.0.1:6379> |
通过键获取元素并附带分数 (withscores): zrangebyscore key -inf +inf withscores
翻译:带分数 withscores
127.0.0.1:6379> zrangebyscore h1 -inf +inf withscores | |
1) "dazuo" | |
2) "500" | |
3) "liuhong" | |
4) "2500" | |
5) "xiaogong" | |
6) "5000" | |
127.0.0.1:6379> |
移除指定键中的 member: zrem key member
127.0.0.1:6379> zrange h1 0 -1 | |
1) "dazuo" | |
2) "liuhong" | |
3) "dagong" | |
4) "xiaogong" | |
127.0.0.1:6379> zrem h1 dagong | |
(integer) 1 | |
127.0.0.1:6379> zrange h1 0 -1 | |
1) "dazuo" | |
2) "liuhong" | |
3) "xiaogong" | |
127.0.0.1:6379> |
通过键获取集合中的元素个数: zcard key
127.0.0.1:6379> zrange h1 0 -1 | |
1) "dazuo" | |
2) "liuhong" | |
3) "xiaogong" | |
127.0.0.1:6379> zcard h1 | |
(integer) 3 | |
127.0.0.1:6379> |
获取指定区间的成员数量: zcount key start end
127.0.0.1:6379> zrange h1 0 -1 withscores | |
1) "dazuo" | |
2) "500" | |
3) "liuhong" | |
4) "2500" | |
5) "xiaogong" | |
6) "5000" | |
127.0.0.1:6379> zcount h1 500 5000 #从 500 到 5000 的存在的元素的个数 | |
(integer) 3 | |
127.0.0.1:6379> |
- 区分总结: set 与 zset 都是 value 不可重复的
- 其中 String , Hash 添加命令都是 set 而 Set , Zset 添加命令是 add
- 其中获取元素个数:
# 三种特殊数据类型
# geospatial 地理位置
<u> 朋友的定位,附近的人,打车距离计算 </u>
Redis 的 Geo 在 Redis3.2 版本就推出了,这个功能可以 <u> 推算地理位置的信息,两地之间的距离,方圆几里的人 </u>
geoadd
#geoadd 添加地理位置 | |
#规则:两级无法直接添加,我们一般会下载城市数据,直接通过 java 程序一次性导入 | |
#参数: key 值 (经度,纬度,名称) | |
#有效的经度从 - 180 度到 180 度 | |
#有效的纬度从 - 85.05112878 度到 85.05112878 度 | |
#当坐标位置超出上述指定范围时,该命令将会返回一个错误,添加时注意以下的格式说明 | |
# 经度 | 纬度 | |
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing | |
(integer) 1 | |
127.0.0.1:6379> |
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 congqi | |
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 congqi | |
"1464070.8051" | |
127.0.0.1:6379> |
127.0.0.1:6379> geodist china:city beijing congqi m #北京到重庆的直线距离以米为单位 | |
"1464070.8051" | |
127.0.0.1:6379> geodist china:city beijing congqi km #北京到重庆的直线距离以千米为单位 | |
"1464.0708" | |
127.0.0.1:6379> geodist china:city beijing congqi mi #北京到重庆的直线距离以英里为单位 | |
"909.7337" | |
127.0.0.1:6379> geodist china:city beijing congqi ft #北京到重庆的直线距离以英尺为单位 | |
"4803381.9063" | |
127.0.0.1:6379> |
georadius 以给定的经纬度为中心,找出某一半径内的元素
我附近的人?(获得所有附近的人的地址,定位!) 通过半径来查询
获得指定数量的人,200
127.0.0.1:6379> georadius china:city 110 30 1000 km #获得以 110 , 30 这个经纬度为中心,方圆 1000km (千米) 内的城市 | |
1) "congqi" | |
2) "hangzhou" | |
127.0.0.1:6379> georadius china:city 110 30 1000 km withdist #显示两个城市的直线距离 | |
1) 1) "congqi" | |
2) "341.9374" | |
2) 1) "hangzhou" | |
2) "977.5143" | |
127.0.0.1:6379> georadius china:city 110 30 1000 km withcoord #显示两个城市的经度纬度 | |
1) 1) "congqi" | |
2) 1) "106.49999767541885376" | |
2) "29.52999957900659211" | |
2) 1) "hangzhou" | |
2) 1) "120.1600000262260437" | |
2) "30.2400003229490224" | |
127.0.0.1:6379> georadius china:city 110 30 1000 km withdist withcoord count 1 #限制显示数量 | |
1) 1) "congqi" | |
2) "341.9374" | |
3) 1) "106.49999767541885376" | |
2) "29.52999957900659211" | |
127.0.0.1:6379> georadius china:city 110 30 1000 km withdist withcoord count 2 | |
1) 1) "congqi" | |
2) "341.9374" | |
3) 1) "106.49999767541885376" | |
2) "29.52999957900659211" | |
2) 1) "hangzhou" | |
2) "977.5143" | |
3) 1) "120.1600000262260437" | |
2) "30.2400003229490224" | |
127.0.0.1:6379> |
georadiusbymember 以某一城市为中心,查找指定范围内的其它城市
翻译: radiusbymember 半径成员
#以某一城市为中心,查找指定范围内的其它城市 | |
127.0.0.1:6379> georadiusbymember china:city beijing 1300 km | |
1) "hangzhou" | |
2) "beijing" | |
127.0.0.1:6379> georadiusbymember china:city beijing 1500 km | |
1) "congqi" | |
2) "hangzhou" | |
3) "beijing" | |
127.0.0.1:6379> georadiusbymember china:city beijing 1000 km | |
1) "beijing" | |
127.0.0.1:6379> |
geohash 返回一个或多个位置元素的 geohash 表示
该命令将返回 11 个字符的 geohash 字符串
#将二维的经纬度转换为一维的字符串,如果两个字符串越相似就代表越近 | |
127.0.0.1:6379> geohash china:city beijing hangzhou | |
1) "wx4fbxxfke0" | |
2) "wtmkn31bfb0" | |
127.0.0.1:6379> geohash china:city beijing congqi hangzhou | |
1) "wx4fbxxfke0" | |
2) "wm5xzrybty0" | |
3) "wtmkn31bfb0" | |
127.0.0.1:6379> |
- geo 底层的实现原理其实就是 Zset, 我们可以使用 Zset 命令来操作 geo
127.0.0.1:6379> zrange china:city 0 -1 #查看地图中全部元素 | |
1) "congqi" | |
2) "hangzhou" | |
3) "beijing" | |
127.0.0.1:6379> zrem china:city congqi #将指定元素从地图中删除 | |
(integer) 1 | |
127.0.0.1:6379> zrange china:city 0 -1 | |
1) "hangzhou" | |
2) "beijing" | |
127.0.0.1:6379> |
# Hyperloglog
什么是基数?
A
B
基数 (不重复的元素):B {1, 3, 5, 7, 8} = 5 , 可以接受误差!
简介
Redis2.8.9 版本就更新了 Hyperloglog 数据结构
Redis Hyperloglog 基数统计的算法
优点:占用的内存是固定的,2^64 不同的元素的基数,只需要废 12KB 内存,如果要从内存角度来比较的话 Hyperloglog 首选!
网页的 UV (一个人访问一个网站多次,但是还是算作一个人) 视频浏览量
传统的方式,set 保存用户的 id, 然后就可以统计 set 中的元素数量作为标准判断
这个方式如果保存大量的 id, 就会比较麻烦,我们的目的是为了计数,而不是保存用户 id
127.0.0.1:6379> PFadd Hyper a b c d e #创建第一组元素 | |
(integer) 1 | |
127.0.0.1:6379> PFadd Hyper1 f g h i j k #创建第二组元素 | |
(integer) 1 | |
127.0.0.1:6379> PFcount Hyper #查看第一组元素个数 | |
(integer) 5 | |
127.0.0.1:6379> PFcount Hyper1 #查看第二组元素个数 | |
(integer) 6 | |
127.0.0.1:6379> PFmerge Hyper2 Hyper Hyper1 #将两组元素以基数进行合并 | |
OK | |
127.0.0.1:6379> PFcount Hyper2 #查看第一组与第二组合并后的数量 | |
(integer) 11 | |
127.0.0.1:6379> PFadd Hyper4 a b c i m k #创建第三组元素 | |
(integer) 1 | |
127.0.0.1:6379> PFmerge Hyper5 Hyper Hyper4 #将第一组与第三组以基数进行合并 | |
OK | |
127.0.0.1:6379> PFcount Hyper5 #查看合并后的数量 | |
(integer) 8 | |
127.0.0.1:6379> |
如果允许容错,那么一定可以使用 Hyperloglog
如果不允许容错,就使用 set 或者自己的数据类型即可
# Bitmaps
位存储
统计疫情感染人数: 0 1 0 1 0 : 1, 被感染 0, 正常
统计用户信息,活跃,不活跃,登录,未登录,打卡,365 打卡,两个状态的都可以使用 Bitmaps
Bitmaps 位图,数据结构!都是操作二进制来进行记录,就只有 0 和 1 两个状态!
365 天 = 365bit 1 字节 = 8bit 接近用户一年的打卡情况为 46 个字节左右
使用 bitmaps 来记录,周一到周日的打卡情况!
周一:1 周二:1 周三:0 周四:1 周五:0 周六:0 周日:1
127.0.0.1:6379> setbit sign 0 1 | |
(integer) 0 | |
127.0.0.1:6379> setbit sign 1 1 | |
(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 0 | |
(integer) 0 | |
127.0.0.1:6379> setbit sign 5 0 | |
(integer) 0 | |
127.0.0.1:6379> setbit sign 6 1 | |
(integer) 0 | |
127.0.0.1:6379> |
查看某一天是否有打卡!
127.0.0.1:6379> getbit sign 1 | |
(integer) 1 | |
127.0.0.1:6379> getbit sign 2 | |
(integer) 0 | |
127.0.0.1:6379> getbit sign 6 | |
(integer) 1 | |
127.0.0.1:6379> |
统计操作,统计 打卡的天数!
127.0.0.1:6379> bitcount sign 0 -1 | |
(integer) 4 | |
127.0.0.1:6379> bitcount sign | |
(integer) 4 | |
127.0.0.1:6379> |
# 事务
Redis 事务本质:一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行!
一次性,顺序型,排它性!执行一系列的命令!
----队列 set set set 执行----
原子性:要么同时成功,要么同时失败
<font color='red'>Redis 事务没有隔离级别的概念 </font>
所有的命令在事务中,并没有直接被执行,只有发起执行命令的时候才会执行!<font color='red' size=4> 执行命令:Exec</font>
<font color='red'>Redis 单条命令是保证原子性的但是 Redis 的事务是不保证原子性!</font>
Redis 的事务:每次执行完毕后事务就会消失,需要再次开启事务
- 开启事务 (multi)
- 命令入队 (...)
- 执行事务 (exec)
正常执行事务!
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> get k2 #通过键获取值 | |
QUEUED | |
127.0.0.1:6379> set k3 v3 #添加键值 | |
QUEUED | |
127.0.0.1:6379> exec #执行事务 | |
1) OK | |
2) OK | |
3) "v2" | |
4) OK | |
127.0.0.1:6379> |
discard 放弃事务!
翻译: discard 丢弃
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> discard #放弃事务 | |
OK | |
127.0.0.1:6379> get k1 #事务队列中的命令都没有被执行 | |
(nil) | |
127.0.0.1:6379> get k2 | |
(nil) | |
127.0.0.1:6379> get k3 | |
(nil) | |
127.0.0.1:6379> |
编译型异常 (代码有问题!命令有错!), 事务中所有的命令都不会被执行!
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> getset k1 | |
(error) ERR wrong number of arguments for 'getset' command | |
127.0.0.1:6379> set k3 v3 | |
QUEUED | |
127.0.0.1:6379> set k4 v4 | |
QUEUED | |
127.0.0.1:6379> exec | |
(error) EXECABORT Transaction discarded because of previous errors. | |
127.0.0.1:6379> get k1 | |
(nil) | |
127.0.0.1:6379> get k4 | |
(nil) | |
127.0.0.1:6379> |
运行时异常 (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 #给一个字符串进行 + 1 执行的时候报错 | |
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) (error) ERR value is not an integer or out of range #虽然第一条命令报错了,但是依旧正常执行流程成功了 | |
2) OK | |
3) OK | |
4) "v3" | |
127.0.0.1:6379> get k2 | |
"v2" | |
127.0.0.1:6379> get k3 | |
"v3" | |
127.0.0.1:6379> |
监控!Watch (乐观锁) (面试常问)
# 悲观锁:
- 很悲观,认为什么时候都会出现问题,无论做什么都会加锁
# 乐观锁:
很乐观,认为什么时候都不会出现问题,所以一般不会加锁,更新数据的时候去判断一下,在此期间是否有人修改过这个数据
获取 version
更新的时候比较 version
Redis 监视测试
正常执行成功!
127.0.0.1:6379> set money 100 | |
OK | |
127.0.0.1:6379> set out 0 | |
OK | |
127.0.0.1:6379> watch money #监视 money 对象,事务执行完毕之后监控就会自动取消 | |
OK | |
127.0.0.1:6379> multi #事务正常结束,数据期间没有发生变动,这个时候就正常执行成功! | |
OK | |
127.0.0.1:6379> decrby money 20 #花掉 20 元 | |
QUEUED | |
127.0.0.1:6379> incrby out 20 #增加花掉记录 20 元 | |
QUEUED | |
127.0.0.1:6379> exec #执行事务命令 | |
1) (integer) 80 | |
2) (integer) 20 | |
127.0.0.1:6379> |
测试多 Terminal 修改值,使用 watch 可以当作 redis 的乐观锁操作!
#终端一号 | |
127.0.0.1:6379> watch money #监视 money | |
OK | |
127.0.0.1:6379> multi #开启事务 | |
OK | |
127.0.0.1:6379> decrby money 10 #花掉 10 元 | |
QUEUED | |
127.0.0.1:6379> incrby out 10 #记录花掉 10 元 | |
QUEUED | |
#终端二号 | |
127.0.0.1:6379> get money #获取余额 | |
"80" | |
127.0.0.1:6379> set money 1000 #设置为 1000 元 | |
OK | |
127.0.0.1:6379> get money #查看设置的余额 | |
"1000" | |
127.0.0.1:6379> | |
#终端一号 | |
127.0.0.1:6379> exec #执行事务命令 | |
(nil) #exec 执行之前,另外一个线程,修改了我们的值,这个时候,就会导致事务执行失败! | |
127.0.0.1:6379> |
- <u>unwatch 解开全部的乐观锁 watch, 当执行了 exec 或者 discard 后 watch 也会解锁 </u>
<font style="color:red"> 如果不进行 watch, 使用多线程进行同样操作 </font>
#Terminal 一 | |
127.0.0.1:6379> get monery | |
"100" | |
127.0.0.1:6379> multi | |
OK | |
127.0.0.1:6379(TX)> decrby monery 20 | |
QUEUED | |
127.0.0.1:6379(TX)> incrby out 20 | |
QUEUED | |
127.0.0.1:6379(TX)> exec | |
#Terminal 二 | |
127.0.0.1:6379> get monery | |
"100" | |
127.0.0.1:6379> set monery 100 | |
OK | |
127.0.0.1:6379> | |
#Terminal 一 | |
127.0.0.1:6379(TX)> exec | |
1) (integer) 80 | |
2) (integer) 20 | |
127.0.0.1:6379> get monery | |
"80" | |
127.0.0.1:6379> |