Redis五大数据类型及三大特殊类型的基本操作(入门)和Redis事务

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上):官网下载

Redis下载

windows在github上下载,停更很久了(官方不建议在windows上开发Redis)https://github.com/microsoftarchive/redis/releases/tag/win-3.2.100

Redis推荐都是在Linux服务器上搭建

Windows安装

1、下载压缩包

2、解压到自己的电脑上

Redis压缩包目录

3、开启服务,双击运行服务即可

4、使用Redis客户端来连接Redis

5、使用Redis

Redis基本操作

Windows下使用简单,但是Redis推荐使用Linux进行开发

测试性能

redis-benchmark是一个压力测试工具(官方自带)

redis-benchmark命令参数

Redis性能测试

简单测试:

测试:50个并发连接  每个并发10000个请求
redis-benchmark -h localhost -p 6379 -c 50 -n 10000

Redis测试

参数解读:

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命令

官方表示,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只有六个命令

Redis 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

期待指正哦😊

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值