Redis入门
说明:这篇文章是在学习b站狂神的Redis教程时,跟着做的笔记,里面可能有一些偷懒或者不全面之处,望指正,抱拳了🤘
概述
Redis是什么?
Redis(Remote Dictionary Server),即远程字典服务!是当下最热门的NoSQL技术之一,也被人们称为结构化数据库
Redis能干嘛?
1、内存存储、持久化,内存中是断电即失的,所以说持久化很重要(rdb、aof)
2、效率高,可以用于高速缓存
3、发布订阅系统
4、地图信息分析
5、计时器、计数器(浏览量!)
6、……
Redis的特性
1、多样的数据类型
2、持久化
3、集群
4、事务
学习中需要用到的东西
1、官网:https://redis.io/
2、Redis中文网:http://www.redis.cn/
3、下载地址(托管在github上):官网下载
windows在github上下载,停更很久了(官方不建议在windows上开发Redis)https://github.com/microsoftarchive/redis/releases/tag/win-3.2.100
Redis推荐都是在Linux服务器上搭建
Windows安装
1、下载压缩包
2、解压到自己的电脑上
3、开启服务,双击运行服务即可
4、使用Redis客户端来连接Redis
5、使用Redis
Windows下使用简单,但是Redis推荐使用Linux进行开发
测试性能
redis-benchmark是一个压力测试工具(官方自带)
redis-benchmark命令参数
简单测试:
测试:50个并发连接 每个并发10000个请求
redis-benchmark -h localhost -p 6379 -c 50 -n 10000
参数解读:
100000 requests completed in 2.16 seconds
50 parallel clients
3 bytes payload
keep alive: 1
100000个请求,使用2.16秒
每次请求50个并发
每次只写入三个字符串
只有一台服务器来处理这些请求,单机性能
77.40% <= 1 milliseconds 第一毫秒处理77.40%
97.29% <= 2 milliseconds 第二毫秒处理97.29%
99.87% <= 3 milliseconds 第三毫秒处理99.87%
100.00% <= 3 milliseconds 所有请求在三毫秒处理完成
41476.57 requests per second 每秒处理41476.57次请求
Redis基础知识
redis默认有16个数据库(在redis.windows.conf中查看)
默认使用的是第0个数据,可以使用select进行切换数据库
Redis的基本命令
######################################################################
切换数据库
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 maize3 #存入一个数据
OK
127.0.0.1:6379[3]> dbsize #数据库大小变为1
(integer) 1
######################################################################
查看当前数据库的key
127.0.0.1:6379[3]> keys * #keys *命令查看当前数据库所有的key
1) "name"
######################################################################
使用flushdb清空当前数据库
127.0.0.1:6379[3]> flushdb #清空当前数据库
OK
127.0.0.1:6379[3]> keys *
(empty list or set)
######################################################################
使用flushall清空所有数据库
127.0.0.1:6379> select 0
OK
127.0.0.1:6379> keys *
1) "key:__rand_int__"
2) "counter:__rand_int__"
3) "name"
4) "mylist"
127.0.0.1:6379> select 3
OK
127.0.0.1:6379[3]> flushall #清空所有数据库
OK
127.0.0.1:6379[3]> select 0
OK
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379>
######################################################################
判断某个key是否存在,存在返回1,不存在返回0
127.0.0.1:6379> exists name
(integer) 0
127.0.0.1:6379> set name maize
OK
127.0.0.1:6379> exists name
(integer) 1
######################################################################
将某个key转移到指定数据库
127.0.0.1:6379> move name 1 #将当前数据库下的name移到数据库1中
(integer) 1
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> keys *
1) "name"
127.0.0.1:6379[1]>
######################################################################
设置key值的过期时长,单位为秒
127.0.0.1:6379> set name maize
OK
127.0.0.1:6379> expire name 20 #expire设置key的过期时长
(integer) 1
127.0.0.1:6379> ttl name #ttl来获取key的剩余时长
(integer) 18
127.0.0.1:6379> ttl name
(integer) 12
127.0.0.1:6379> ttl name
(integer) 7
127.0.0.1:6379> ttl name
(integer) 3
127.0.0.1:6379> ttl name #剩余时长为-2,说明已经过期
(integer) -2
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> setex key3 20 hello #在创建key时设置key的过期时长(20s后过期),单位为秒
OK
127.0.0.1:6379> ttl key3
(integer) 16
######################################################################
移除指定key
127.0.0.1:6379> set name maize
OK
127.0.0.1:6379> get name
"maize"
127.0.0.1:6379> del name
(integer) 1
127.0.0.1:6379> get name
(nil)
######################################################################
查看指定key的类型(type)
127.0.0.1:6379> set name maize
OK
127.0.0.1:6379> set age 0
OK
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> type name
string
127.0.0.1:6379> type age
string
更多的命令可以在官网查询
官方表示,Redis是基于内存操作的,CPU不是Redis的瓶颈,Redis的瓶颈时根据机器的内存和网络的带宽。
Redis为什么单线程还这么快?
redis是将所有的数据全部放在内存中,所以说使用单线程去操作效率就是最高的(多线程会有CPU的上字阿文切换行为,这是一个耗时的操作),对于内存系统来说,如果没有上下文切换,效率最高。
Redis String
######################################################################
在指定key后追加字符串(append)
当key不存在时,会创建一个key,并将字符串赋值给key
127.0.0.1:6379> get name
"maize"
127.0.0.1:6379> append name -j #在name后添加"-j"
(integer) 7 #添加完成后的字符串长度
127.0.0.1:6379> get name
"maize-j"
######################################################################
实现i++和i–的操作
127.0.0.1:6379> set views 0
OK
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> incr views #将views+1,即自增1
(integer) 1
127.0.0.1:6379> get views
"1" #views从原来的0变为1
127.0.0.1:6379> decr views #将views-1,自减1
(integer) 0
127.0.0.1:6379> get views
"0" #views从原来的1变为0
######################################################################
设置增长步长(即i+n),和减少步长(i-n)
127.0.0.1:6379> incrby views 10 #views+10
(integer) 10
127.0.0.1:6379> get views
"10"
127.0.0.1:6379> decrby views 5 #views-5
(integer) 5
127.0.0.1:6379> get views
"5"
######################################################################
截取字符串
127.0.0.1:6379> get name
"maize-jhelloworld"
127.0.0.1:6379> getrange name 2 5 #相当于java的subString方法,截取索引从2-5的字符串
"ize-"
127.0.0.1:6379> getrange name 0 -1 #范围为0到-1表示查看所有的字符串
"maize-jhelloworld"
127.0.0.1:6379> getrange name 2 -1 #范围为2到-1表示查看索引为2到字符串末尾的片段
"ize-jhelloworld"
######################################################################
给字符串片段赋值
127.0.0.1:6379> set key2 abcdefghijkl
OK
127.0.0.1:6379> get key2
"abcdefghijkl"
127.0.0.1:6379> setrange key2 1 xxx #从索引为1的字符开始替换,替换的长度和要替换的字符串长度相等
(integer) 12
127.0.0.1:6379> get key2
"axxxefghijkl"
127.0.0.1:6379>
######################################################################
key不存在时,才能设置,否则设置失败(返回值为0)
127.0.0.1:6379> setnx mykey hello
(integer) 1
127.0.0.1:6379> get mykey
"hello"
127.0.0.1:6379> setnx mykey world
(integer) 0
127.0.0.1:6379> get mykey
"hello"
######################################################################
批量设置和批量获取值
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379> keys *
1) "views"
2) "k3"
3) "k2"
4) "k1"
5) "name"
6) "age"
7) "key2"
8) "mykey"
127.0.0.1:6379> mget k1 k2 k3
1) "v1"
2) "v2"
3) "v3"
######################################################################
批量添加数据原子性操作
127.0.0.1:6379> keys *
1) "k2"
2) "k1"
3) "k3"
127.0.0.1:6379> msetnx k1 v1 k4 v4 #有重复的key值,设置不会成功,原子性操作,因此k1和k4的值设置都不会成功
(integer) 0
127.0.0.1:6379> keys * #此处没有k4的值
1) "k2"
2) "k1"
3) "k3"
######################################################################
对象的存储
127.0.0.1:6379> set user:2 {name:cyj,age:2} #设置一个user:2对象,值为json字符来保存一个对象
OK
127.0.0.1:6379> get user:2
"{name:cyj,age:2}" #获取到的值也为json字符串
# 这里的key是一个巧妙的设计,user:{id}:{filed},如此设计在Redis中是完全ok的
127.0.0.1:6379> mset user:1:name maize user:1:age 0
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "maize"
2) "0"
######################################################################
获取值并设置值
#getset命令是先get再set,因此在首次设置时,获取到的值为空
#getset命令 非首次设置时(即存在值时),会先获取当前值,再对值进行修改
127.0.0.1:6379> getset name maize
(nil)
127.0.0.1:6379> get name
"maize"
127.0.0.1:6379> getset name cyj
"maize"
127.0.0.1:6379> get name
"cyj"
Redis List
######################################################################
在Redis中,有关于list的操作是以l开头的,在push时是以l或r开头的,l表示从左边push,r表示从右边push (push时是可以存在重复值的)
127.0.0.1:6379> lpush list one #将一个值或多个值插入list的头部
(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"
127.0.0.1:6379> rpush list four #将一个值或多个值插入list的尾部,从右边push时,可以作为队列来对待(先进先出)
(integer) 4
127.0.0.1:6379> rpush list five
(integer) 5
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "four"
5) "five"
######################################################################
跟push对应的,移除一个元素也有左右之分,为lpop和rpop(移除列表中对应方向的第一个元素)
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "four"
5) "five"
127.0.0.1:6379> lpop list #移除左边的第一个元素
"three"
127.0.0.1:6379> rpop list #移除右边的第一个元素
"five"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
3) "four"
######################################################################
根据下标获取某个元素的值,lindex
127.0.0.1:6379> lindex list 0
"two"
127.0.0.1:6379> lindex list 2
"four"
######################################################################
lLen获得当前list的长度(存入多少个值)
127.0.0.1:6379> llen list
(integer) 3
######################################################################
lrem,移除list中指定的值
127.0.0.1:6379> lpush list one
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "one"
2) "two"
3) "one"
4) "four"
127.0.0.1:6379> lrem list 1 two #只移除一个two (精确匹配)
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "one"
2) "one"
3) "four"
127.0.0.1:6379> lrem list 2 one #移除两个one
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "four"
######################################################################
截取list中的部分值
127.0.0.1:6379> rpush list hello1
(integer) 1
127.0.0.1:6379> rpush list hello2
(integer) 2
127.0.0.1:6379> rpush list hello3
(integer) 3
127.0.0.1:6379> rpush list hello4
(integer) 4
127.0.0.1:6379> ltrim list 1 2 #只截取其中索引1到2的元素,剩余的其他元素被移除,相当于Java中的trim方法
OK
127.0.0.1:6379> lrange list 0 -1
1) "hello2"
2) "hello3"
######################################################################
rpoplpush 移除列表中的最后一个元素,并将该元素添加到另一个list的头部
127.0.0.1:6379> lrange list 0 -1
1) "hello1"
2) "hello2"
3) "hello3"
4) "hello4"
127.0.0.1:6379> rpoplpush list mylist #list为源list,mylist为目的list
"hello4"
127.0.0.1:6379> lrange list 0 -1 #原list中已将该值移除
1) "hello1"
2) "hello2"
3) "hello3"
127.0.0.1:6379> lrange mylist 0 -1 #新list中头部添加该值
1) "hello4"
######################################################################
lset,将列表中指定下标的值替换为新的元素,当下标指定元素存在时,替换,不存在时,报错。
127.0.0.1:6379> lrange list 0 -1
1) "hello1"
2) "hello2"
3) "hello3"
127.0.0.1:6379> lset list 1 hello
OK
127.0.0.1:6379> lrange list 0 -1
1) "hello1"
2) "hello"
3) "hello3"
######################################################################
linsert,在指定的元素前面或后面插入指定的元素。linsert的格式如下:linsert key BEFORE|AFTER pivot value ,由BEFORE|AFTER指定从前或从后插入, pivot指定list中具体元素的值,即从list的哪个元素的前或后插入,当list中存在多个pivot指定值,则选中下标小的元素
127.0.0.1:6379> lrange list 0 -1
1) "hello1"
2) "hello"
3) "hello3"
127.0.0.1:6379> linsert list before hello hello2
(integer) 4
127.0.0.1:6379> linsert list after hello3 hello4
(integer) 5
127.0.0.1:6379> lrange list 0 -1
1) "hello1"
2) "hello2"
3) "hello"
4) "hello3"
5) "hello4"
小结:Redis中的链表实际上是一个链表,before Node after,left,right都可以进行值的插入,在两边插入或者值的改动,效率最高,中间元素,相对来说效率会相对来说低一些。
Redis Set
set是无需不重复集合,跟set有关的操作,命令前都有s
######################################################################
sadd,向set中插入值,set不存在时,创建set。smembers查询set集合中的所有元素
127.0.0.1:6379> sadd myset hello
(integer) 1
127.0.0.1:6379> sadd myset world
(integer) 1
127.0.0.1:6379> smembers myset
1) "world"
2) "hello"
######################################################################
判断元素是否是某set中的元素,值为0表示是set中的元素,值为1表示不是set中的元素
127.0.0.1:6379> smembers myset
1) "world"
2) "hello"
127.0.0.1:6379> sismember myset maize
(integer) 0
127.0.0.1:6379> sismember myset hello
(integer) 1
######################################################################
scard,获取当前set中元素的个数
127.0.0.1:6379> scard myset
(integer) 2
######################################################################
srem,移除set中指定元素
127.0.0.1:6379> smembers myset
1) "hello2"
2) "hello1"
3) "world"
4) "hello"
127.0.0.1:6379> srem myset hello
(integer) 1
127.0.0.1:6379> smembers myset
1) "hello2"
2) "hello1"
3) "world"
######################################################################
srandmember,随机抽选出指定个数据的元素
127.0.0.1:6379> smembers myset
1) "hello5"
2) "hello6"
3) "hello1"
4) "hello2"
5) "hello3"
6) "hello"
7) "world"
8) "hello4"
127.0.0.1:6379> srandmember myset #未指定个数时,默认为抽选出一个
"hello5"
127.0.0.1:6379> srandmember myset 2 #指定个数时,根据指定个数抽选元素
1) "hello5"
2) "hello2"
127.0.0.1:6379> srandmember myset 2
1) "hello5"
2) "hello"
######################################################################
spop,弹出元素
127.0.0.1:6379> smembers myset
1) "hello5"
2) "hello6"
3) "hello1"
4) "hello2"
5) "hello3"
6) "hello"
7) "world"
8) "hello4"
127.0.0.1:6379> spop myset
"hello6"
127.0.0.1:6379> spop myset
"world"
127.0.0.1:6379> spop myset 2
1) "hello3"
2) "hello"
127.0.0.1:6379> smembers myset
1) "hello5"
2) "hello1"
3) "hello2"
4) "hello4"
######################################################################
smove,将指定值从源集合移动到目标集合中
127.0.0.1:6379> smembers myset
1) "hello5"
2) "hello1"
3) "hello2"
4) "hello4"
127.0.0.1:6379> sadd myset2 maize cyj
(integer) 2
127.0.0.1:6379> smove myset myset2 hello1
(integer) 1
127.0.0.1:6379> smembers myset
1) "hello5"
2) "hello2"
3) "hello4"
127.0.0.1:6379> smembers myset2
1) "hello1"
2) "maize"
3) "cyj"
######################################################################
sdiff,两集合的差集
127.0.0.1:6379> smembers k1
1) "b"
2) "d"
3) "a"
4) "c"
127.0.0.1:6379> smembers k2
1) "g"
2) "e"
3) "f"
4) "c"
5) "d"
127.0.0.1:6379> sdiff k1 k2 #所求为k1相当于k2的差集
1) "b"
2) "a"
######################################################################
sinter,两集合的交集
127.0.0.1:6379> smembers k1
1) "b"
2) "d"
3) "a"
4) "c"
127.0.0.1:6379> smembers k2
1) "g"
2) "e"
3) "f"
4) "c"
5) "d"
127.0.0.1:6379> sinter k1 k2
1) "d"
2) "c"
######################################################################
sunion,两集合的并集
127.0.0.1:6379> sunion k1 k2
1) "b"
2) "g"
3) "d"
4) "a"
5) "f"
6) "c"
7) "e"
Redis Hash
可以理解为一个map,在前面都是学习的key-value,这里是key-map,hash的命令是以h开头的,其命令和前面都是大同小异的
######################################################################
hash的存值和取值
127.0.0.1:6379> hset myhash field1 maize
(integer) 1
127.0.0.1:6379> hget myhash field1
"maize"
######################################################################
hmset,hmget,同时设置和取出多个值
127.0.0.1:6379> hmset myhash field2 cyj field3 j
OK
127.0.0.1:6379> hmget myhash field1 field2 field3
1) "maize"
2) "cyj"
3) "j"
######################################################################
hgetall,获取hash中所有的值
127.0.0.1:6379> hgetall myhash
1) "field1"
2) "maize"
3) "field2"
4) "cyj"
5) "field3"
6) "j"
hdel删除hash中的元素,hlen计算hash的长度,hexists,判断hash中指定字段是否存在
hkeys,获取hash中所有字段(field),hvals,获取hash中所有的值(value)
Redis Zset(有序集合)
在set的基础上加上一个值,set k1 v1 zset k1 score1 v1
zset的命令以z开头
######################################################################
zset中添加一个值和添加多个值
127.0.0.1:6379> zadd myzset 1 one
(integer) 1
127.0.0.1:6379> zadd myzset 3 three 2 two
(integer) 2
127.0.0.1:6379> zrange myzset 0 -1
1) "one"
2) "two"
3) "three"
######################################################################
zset中元素的排序
127.0.0.1:6379> zrange myzset 0 -1
1) "one"
2) "two"
3) "three"
127.0.0.1:6379> zrangebyscore myzset -inf +inf #-inf表示负无穷,+inf表示正无穷,也可以写其他的取值范围
1) "one"
2) "two"
3) "three"
######################################################################
zrem,移除有序集合zset中的指定元素
127.0.0.1:6379> zrange myzset 0 -1
1) "one"
2) "two"
3) "three"
127.0.0.1:6379> zrem myzset three
(integer) 1
127.0.0.1:6379> zrange myzset 0 -1
1) "one"
2) "two"
######################################################################
zcount,获取指定区间的成员数量
127.0.0.1:6379> zrange myzset 0 -1
1) "one"
2) "two"
3) "three"
4) "four"
5) "five"
6) "six"
7) "seven"
127.0.0.1:6379> zcount myzset 0 5
(integer) 5
有关Zset的命令可以在http://www.redis.cn/commands.html#sorted_set查看
三种特殊数据类型
geospatial 地理位置
GEO只有六个命令
######################################################################
geoadd,添加地理位置 geoadd 【key】 【经度】 【纬度】 【城市名称】
规则:两极无法直接添加,我们一般会下载城市数据,直接通过java一次性导入
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.08 22.54 shenzhen
(integer) 1
127.0.0.1:6379> geoadd china:city 120.15 30.28 hangzhou
(integer) 1
127.0.0.1:6379> geoadd china:city 108.94 34.26 xian
(integer) 1
######################################################################
geopos,获取某个地址的地理位置,是一个坐标值
127.0.0.1:6379> geopos china:city beijing #获取指定城市的经度和纬度
1) 1) "116.39999896287918"
2) "39.900000091670925"
127.0.0.1:6379> geopos china:city chongqing shanghai
1) 1) "106.49999767541885"
2) "29.529999579006592"
2) 1) "121.47000163793564"
2) "31.229999039757836"
######################################################################
geodist,获得两个地理位置之间的距离,如果两个位置其中一个不存在,命令返回空
127.0.0.1:6379> geodist china:city beijing chongqing
"1464070.8051"
127.0.0.1:6379> geodist china:city beijing chongqing km #计算北京和上海的km值
"1464.0708"
######################################################################
georadius,以给定经纬度为中心,找出某一半径的元素
用法:查找附近的人
georadius key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key] https://cloud.tencent.com/developer/section/1374022
georadius 【key】 【经度】【纬度】【距离】【单位】【显示距离(可选)】【显示坐标(可选)】【count筛选几个用户】
127.0.0.1:6379> georadius china:city 110 30 1000 km
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
127.0.0.1:6379> georadius china:city 110 30 800 km withcoord
1) 1) "chongqing"
2) 1) "106.49999767541885"
2) "29.529999579006592"
2) 1) "xian"
2) 1) "108.9399978518486"
2) "34.2599996441893"
127.0.0.1:6379> georadius china:city 110 30 800 km withcoord withdist withhash
1) 1) "chongqing"
2) "341.9374" #距离
3) (integer) 4026042091628984
4) 1) "106.49999767541885" #经度
2) "29.529999579006592" #纬度
2) 1) "xian"
2) "484.2186"
3) (integer) 4040115344891327
4) 1) "108.9399978518486"
2) "34.2599996441893"
######################################################################
georadiusbymember,查找key中的城市的指定半径内的城市元素,会查询出自己
127.0.0.1:6379> georadiusbymember china:city beijing 1000 km withcoord withdist
1) 1) "beijing"
2) "0.0000"
3) 1) "116.39999896287918"
2) "39.900000091670925"
2) 1) "xian"
2) "911.3409"
3) 1) "108.9399978518486"
2) "34.2599996441893"
######################################################################
geohash,返回一个或多个元素的geohash表示(用的较少)
#将二维的经纬度转换为一维字符串,如果两个字符串越接近,那么距离越近
127.0.0.1:6379> geohash china:city beijing chongqing
1) "wx4fbxxfke0"
2) "wm5xzrybty0"
GEO 底层的实现原理起始就是Zset,我们可以使用zset命令来操作geo,例如查看范围内的元素,删除元素的命令等等
Hyperloglog
什么是基数?
集合A{1,3,5,7,8,7}
集合B{1,3,5,7,8}
基数(不重复元素)=5,可以接受误差
Redis Hyperloglog是基数统计算法,它最大的有点就是占用的内存是固定的,2^64不同的元素基数,只需要12kb的内存,从内存的角度来考虑,在集合不重复计数中,Hyperloglog是首选。
Hyperloglog的命令都是以PF开头的
######################################################################
PFadd,添加元素,PFCOUNT,元素计数(不重复的元素个数)
127.0.0.1:6379> PFadd mykey a b c d e f g h
(integer) 1
127.0.0.1:6379> PFCOUNT mykey
(integer) 8
######################################################################
pfmerge,合并两个Hyperloglog的元素(取并集)
PFMERGE 【目标key】 【源keys,多个】
127.0.0.1:6379> PFadd mykey a b c d e f g h
(integer) 1
127.0.0.1:6379> PFCOUNT mykey
(integer) 8
127.0.0.1:6379> PFADD mykey2 g h i j k l m
(integer) 1
127.0.0.1:6379> PFCOUNT mykey2
(integer) 7
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2
OK
127.0.0.1:6379> pfcount mykey3
(integer) 13
Bitmaps
位存储
bitmaps位图,数据结构,都是操作二进制位来进行记录,只有0和1两个状态
######################################################################
setbit,存储bitmaps(这里应用场景是模拟一周登录情况)
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 1
(integer) 0
127.0.0.1:6379> setbit sign 5 0
(integer) 0
127.0.0.1:6379> setbit sign 6 0
(integer) 0
######################################################################
getbit,查看某天登录情况
127.0.0.1:6379> getbit sign 2
(integer) 0
######################################################################
bitcount,统计打开天数
127.0.0.1:6379> bitcount sign #未指定范围,默认查询所有
(integer) 4
127.0.0.1:6379> bitcount sign 0 4
(integer) 4
Redis事务
Redis事务本质:一组命令的集合,一个事务中的所有命令都会被序列化,在事务执行过程中,会按顺序执行
Redis单条命令是保证原子性的,但Redis事务是不保证原子性的,且Redis事务没有隔离基本别概念
Redis事务中,所有的命令在事务中,并没有直接被执行,只有发起执行命令的时候才会执行。
Redis的事务:
1、开启事务(multi)
2、命令入队(…)
3、执行事务(exec)
######################################################################
一次事务的执行过程(每次事务在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
######################################################################
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 k4 v4
QUEUED
127.0.0.1:6379> DISCARD
OK
127.0.0.1:6379> get k4 #事务队列中命令都不会被执行,k4未被存入
(nil)
编译时错误(命令有错误),事务中所有的命令都不会被执行
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> getset k3 #错误的命令
(error) ERR wrong number of arguments for 'getset' command
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)
运行时错误(例如java中经常模拟的1/0),执行命令时,其他的命令是可以正常执行的,错误命令抛出异常
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr k1 #对字符串进行+1操作,编译时不出错,运行时出错
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range #命令报错
2) OK #事务中其他语句正常执行
127.0.0.1:6379> get k3 #语句执行成功,成功将字符串存入
"v3"
监控
悲观锁:很悲观,认为什么时候都会出问题,无论做什么都会加锁
乐观锁:很乐观,认为什么时候都不会出问题,因此不会上锁,更新数据的时候判断一下,在此期间是否有人修改数据,在mysql中获取version,更新的时候比较version
Redis监视测试 (Redis的watch本身就是乐观锁)
正常执行成功
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
QUEUED
127.0.0.1:6379> incrby out 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 80
2) (integer) 20
事务执行失败
一个客户端监视了money,且已开启事务,并将命令入队,但并未执行
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby money 20
QUEUED
在另一个客户端,直接修改了money的值
127.0.0.1:6379> get money
"100"
127.0.0.1:6379> set money 120
OK
这时在监视money的客户端执行事务,会发现事务返回为空,因为检测到money的值被修改,事务将执行失败
127.0.0.1:6379> exec
(nil)
如果修改失败,先解锁,再重新获取锁
127.0.0.1:6379> unwatch #如果事务执行失败,就先解锁
OK
127.0.0.1:6379> watch money #获取最新的值,再次监视,select version
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> incrby out 10
QUEUED
127.0.0.1:6379> exec #比对监视的值是否发生改变,如果未发生改变,则执行成功,否则执行失败
1) (integer) 90
2) (integer) 10
`
运行时错误(例如java中经常模拟的1/0),执行命令时,其他的命令是可以正常执行的,错误命令抛出异常
监控
悲观锁:很悲观,认为什么时候都会出问题,无论做什么都会加锁
乐观锁:很乐观,认为什么时候都不会出问题,因此不会上锁,更新数据的时候判断一下,在此期间是否有人修改数据,在mysql中获取version,更新的时候比较version
Redis监视测试 (Redis的watch本身就是乐观锁)
正常执行成功
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
QUEUED
127.0.0.1:6379> incrby out 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 80
2) (integer) 20
事务执行失败
一个客户端监视了money,且已开启事务,并将命令入队,但并未执行
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby money 20
QUEUED
在另一个客户端,直接修改了money的值
127.0.0.1:6379> get money
"100"
127.0.0.1:6379> set money 120
OK
这时在监视money的客户端执行事务,会发现事务返回为空,因为检测到money的值被修改,事务将执行失败
127.0.0.1:6379> exec
(nil)
如果修改失败,先解锁,再重新获取锁
127.0.0.1:6379> unwatch #如果事务执行失败,就先解锁
OK
127.0.0.1:6379> watch money #获取最新的值,再次监视,select version
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> incrby out 10
QUEUED
127.0.0.1:6379> exec #比对监视的值是否发生改变,如果未发生改变,则执行成功,否则执行失败
1) (integer) 90
2) (integer) 10
期待指正哦😊