Redis第1讲——入门简介

Java并发编程的总结和学习算是告一段落了,这段时间思来想去,还是决定把Redis再巩固和学习一下。毕竟Redis不论是在面试还是实际应用中都是极其重要的,在面试中诸如Redis的缓存问题、热key、大key、过期策略、持久化机制等;还有在实际应用中的Redis缓存、分布式锁、Reids实现排行榜、分布式限流功能、Redis做延迟队列、消息队列、发布订阅等。相信大家对这并不陌生,而作者想要做的就是在其基础上,把它们进行总结、整理、扩展并深入

一、为什么要学Redis?

目前大多数系统都是集群部署,那么在用传统的锁和缓存时就会出现以下问题:

  • 缓存失效:在分布式集群中,每个节点之间的数据不是共享的,而本地缓存是本地的,因此,在一个节点上的本地缓存可能会缓存旧数据,而在另一个节点上的数据已经更新。这就会导致数据不一致性问题,从而影响系统的正确性。
  • 互斥锁问题:传统的锁(Synchronized、Lock)是基于但服务实现的,多个服务之间无法共享锁的状态,简单来说就是锁失效,那么也会出现数据不一致等问题。

可以通过Redis的分布式缓存和分布式锁来解决上述问题。

还有就是有的功能使用关系型数据库来解决可能很复杂,而用Redis这种非关系型数据库实现就比较简单,比如聊天室功能:

  • 如果用关系型数据库,一般都需要使用JOIN来进行多表查询,而在线的聊天室通常是非常频繁的读写操作,JOIN多表查询再加上频繁读写,不仅实现起来比较复杂,性能也很差。
  • 如果用Redis,那就可以非常容易实现而且性能也远超关系型数据库。比如用Redis的List或Hash数据结构,将聊天室中的所有消息按照时间戳存储在一个列表中,用户发送的消息可以通过Redis的集合或队列存储,再使用Redis的发布订阅功能就可以实现消息的广播和接收。

二、什么是Redis?

Redis(Remote Dictionary Server),即远程字段服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

Redis的作者——Antirez最初创建Redis是为了扩展实时日志分析工具LLOOGG,早先LLOOGG是基于MySQL的,但很快就遇到了性能瓶颈,Antirez相信使用内存可以解决这个问题,于是便开始了Redis的开发工作(原名为LLOOGG Memory Database),之后便有了这个功能强大、高性能、可扩展、易于使用的存储系统。

ps:这就是大佬思维,我们再遇到问题时第一想法是网上搜,而大佬的思维是自己开发一个,膜拜🤗。

  • Redis最重要的功能是做缓存,查询效率远高于Mysql数据库。
  • Redis是基于内存存储数据的,读写性能贼高。
  • Redis 4.0之前,读的速度是110000次/S,写的速度是81000次/S;Redis6.0引入多线程后性能几乎翻倍,读的速度可达200000次/s,写的速度可达170000次/S。
  • Redis不仅仅是Key-Value存储结构,针对Value还有多种数据结构。
  • Redis也提供了相应的持久化方案(RDB、AOF)。
  • Redis提供了主从复制、哨兵模式和Cluster等方案实现高可用。
  • Redis提供了强大的功能,比如事务、发布订阅、Lua脚本等。

优点还有很多,不一一列举了....

Reids是一种非关系型数据库(NoSql),此外,常见的还有以下几种:

  • key-value存储:将数据以键值对的形式存储,常用于缓存,比如Reids、MemCache等。
  • 文档型存储:以类似于JSON或XML格式的文档形式存储,适用于存储和查询复杂的、半结构化的数据,比如Elasticsearch、MongDB等。
  • 面向列存储:将数据按列存储,而不是按行,适用于大规模数据处理和分析,比如HBase、Apache Cassandra等。
  • 图形化存储:以图的形式存储数据,适用于需要处理复杂关系和图算法的常见,比如Neo4j、OrientDB等。

三、Redis为什么要自己定义SDS?

从这开始就正式介绍Redis了。我们知道Redis是C语言实现的,但他并没有直接使用C语言中的字符数组的方式来实现字符串,而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS),并将SDS用作Redis的默认字符串表示,这是为什么呢?

我们先回顾下C语言中字符串的实现。C语言使用长度N+1的字符串数组来表示长度未N的字符串,并且字符串数组的最后一个元素总是为空字符'\0'

那么这就会产生两个问题:

  • 首先它就不能保存任意内容了,至少'\0'就不能保存了,因为遇到它时就直接截断了。
  • 其次就是C语言中用'\0'来表示字符串结束的方式,所以计算长度、字符串追加等操作,都需要从头遍历,直到遇到'\0'才会返回长度或追加,那么它的性能就不是很高了。

因此,Redis自己定义了一个SDS来解决以上两个问题:

  • 在用字符串数据表示字符串的同时,在这个字符串中增加一个表示分配给该字符串数组的总长度alloc字段和一个表示字符串现有长度len字段,这样在获取长度的时候就不依赖于'\0'了,直接返回len就行了。
  • 在字符串做追加操作时,只需要判断新追加的部分的len加上已有的len是否大于alloc,如果超过就重新申请新空间,反之直接追加就行了。

ps:此外,SDS还被用作缓冲区(buffer):AOF模块中的AOF缓冲区,客户端状态中的输入缓冲区。

四、Redis的数据类型与数据结构

我们常见的数据类型比如string、hash、list、set、zset等类型其实在Redis中是一个个对象,是由简单动态字符串(SDS)、哈希表、整数集合、压缩列表、跳跃表等数据结构实现的。

Reids中的每个对象都由一个redisObject结构表示,该结构中和保存数据有关的有三个属性:

  • type:记录对象的类型,比如字符串对象(string)、列表对象(list)等,可用type key命令查看value的存储类型。
  • encoding:记录对象使用的编码,比如int、embstr等(下面会提到)。
  • ptr:ptr指针指向对象底层实现数据结构,这些结构则是由encoding决定的。

对应关系如下(不是很全但够用):

上图为Redis常用的5种基本类型及Reids 5.0版本的stream类型和Redis的9种编码方式和7种底层数据结构对应的关系。下面我们详细的介绍下。

五、九种数据类型

5.1 String类型

5.1.1 简介

String类型是Redis最基本的数据类型,它可以存储任意类型的数据,比如数字、文本或者是序列化后的对象,最大可存储512MB的数据。

底层实现是SDS,由长度、空闲空间和字节数组三部分组成,并且有3种编码方式:

  • int编码:用于存储整型数据,是以Long类型存储,未使用SDS类型。

  • embstr编码:当存储的字符串长度小于等于32字节,用embstr编码。

  •  raw编码:当存储的字符串长度大于32字节,用raw编码。

5.1.2 应用场景

应用场景还是非常广泛的,比如:

  • 缓存:提高性能,降低数据库压力。
  • 计数器:利用incr和decr命令实现原子性加减操作。
  • 分布式锁:利用setnx命令。
  • 限流:利用计数器的功能来实现限流。

5.1.3 常用命令

  • SET key value:设置指定 key 的值为指定的 value。
  • GET key:获取指定 key 的值。
  • INCR key:将指定 key 的值加 1,并返回加 1 后的值。
  • DECR key:将指定 key 的值减 1,并返回减 1 后的值。
  • APPEND key value:将指定 key 的值追加指定的 value。
  • STRLEN key:返回指定 key 的值长度。
  • SETEX key seconds value:设置指定 key 的值,并指定过期时间,单位为秒。

5.2 hash类型

5.2.1 简介

hash是一个键值对集合,可以存储多个字段和值,简单的说它的value就是一个Map集合,一个hash最多可以存储2^32-1个字段。

底层实现其实有三种:

  • ziplist:压缩列表,当ziplist元素超过512个单个元素超过64字节(可在redis的配置文件中设置),会转为hashtable。

  • listpack:紧凑列表,在Redis7.0之后,listpack取代ziplist,同样,到达上述阈值会转化为hashtable。

  • hashtable:哈希表,类似map。

下面我们来看下它们编码转换的情况:

5.2.2 应用场景

  • 存储对象或实体属性:比如用户信息、商品信息。

  • 存储配置信息:比如连接字符串、端口号、默认配置等。

5.2.3 常用命令

  • HSET:设置 hash 中的字段和值。用法:HSET key field value
  • HGET:获取指定 hash 字段的值。用法:HGET key field
  • HMSET:同时设置 hash 中多个字段和值。用法:HMSET key field1 value1 [field2 value2 …]
  • HMGET:获取指定 hash 中多个字段的值。用法:HMGET key field1 [field2 …]
  • HGETALL:获取 hash 中所有字段和值。用法:HGETALL key
  • HDEL:删除 hash 中的一个或多个字段。用法:HDEL key field1 [field2 …]
  • HEXISTS:检查指定的字段在 hash 中是否存在。用法:HEXISTS key field
  • HINCRBY:将 hash 中指定字段的值增加指定增量。用法:HINCRBY key field increment
  • HINCRBYFLOAT:将 hash 中指定字段的浮点数值增加指定增量。用法:HINCRBYFLOAT key field increment
  • HKEYS:获取 hash 中所有的字段。用法:HKEYS key
  • HVALS:获取 hash 中所有的值。用法:HVALS key
  • HLEN:获取 hash 中的字段数量。用法:HLEN key
  • HSCAN:迭代遍历 hash 中的字段和值。用法:HSCAN key cursor [MATCH pattern] [COUNT count]

5.3 List类型

5.3.1 简介

List是一个存取有序的字符串列表,按照插入顺序排序,有下标,并且支持两端插入或删除元素。一个list的键最多可以存储2^32-1个元素。

底层实现是linkedlist和zipList:

  • ziplist:当ziplist的结点超过512个或节点内存大于64字节时会转为linkedlist,当然,这个在Redis配置文件中也可以修改。
  • linkedlist:双端链表。

下面介绍一下,不同版本的Redis其list的实现:

  • Reids3.2之前:list使用linkedlist和ziplist。
  • Reids3.2至Redis7.0:list使用的是quicklist,linkedlist和ziplist的结合。
  • Reids7.0之后:list使用的也是quicklist,不过将ziplist转为listpack,其实就是listpack和linkedlist结合。

5.3.2 应用场景

  • 消息队列:将消息以先后顺序添加到list中,然后可以用lpop命令从列表的左侧弹出消息并处理。
  • 实时排行榜:将用户的得分或其它评价指标作为list的值,在该指标上进行排序。通过lpush、rpush和ltrim命令可以动态地更新排行榜。
  • 发布/订阅:发布者使用rpush命令将消息推送到列表中,订阅者用blpop或brpop命令在列表上进行阻塞弹出接收消息。
  • 历史记录:将聊天记录、日志信息等存储为list的值,使用lpush和lrange命令可以存储和查询最近的几条消息。

5.3.3 常用命令

  • LPUSH:从列表的左侧添加一个或多个元素。用法:LPUSH key value1 [value2 …]
  • RPUSH:从列表的右侧添加一个或多个元素。用法:RPUSH key value1 [value2 …]
  • LPOP:从列表的左侧弹出第一个元素。用法:LPOP key
  • RPOP:从列表的右侧弹出最后一个元素。用法:RPOP key
  • LRANGE:获取列表中指定范围的元素。用法:LRANGE key start stop
  • LINDEX:获取列表中指定索引位置的元素。用法:LINDEX key index
  • LLEN:获取列表的长度(即元素数量)。用法:LLEN key
  • LTRIM:修剪列表,只保留指定范围内的元素,其余元素删除。用法:LTRIM key start stop
  • LINSERT:在列表中指定元素的前面或后面插入一个新元素。用法:LINSERT key BEFORE|AFTER pivot value
  • LREM:从列表中删除指定数量的匹配元素。用法:LREM key count value

5.4 set类型

5.4.1 简介

set是一个无序的字符串集合,不允许重复,没有下标,一个set类型的键最多可以存储2^32-1个元素。

底层实现为intset和hashtable:

  • intset:当使用intset进行存储时,redis会自动的进行递增排序,因此,只存整型的话,其是一个有顺序的结构,但是并非只存整型数据就一直用intset,当整型的元素个数超过512个元素时,会转为hashtable。当存的是非整形时,也会转为hashtable进行存储。

  • hashtable:与hash类型的哈希表相同,将元素存储在一个数组中,并通过哈希函数计算元素在数组中的索引。

5.4.2 应用场景

  • 去重:利用sadd和scard命令实现元素的去重并计数。
  • 粉丝和关注系统:使用两个set分别存储用户的关注者和粉丝,使用sadd命令和srem命令来添加和移除关注,使用sinter和sunion命令可以计算共同关注和推荐关注。
  • 抽奖:将用户存储在set中,使用sadd和srem命令增加和删除参与资格,通过srandmember命令从set中随机选择一个或多个参与者。

5.4.3 常用命令

  • SADD:向 set 中添加一个或多个元素。用法:SADD key member1 [member2 …]
  • SREM:从 set 中删除一个或多个元素。用法:SREM key member1 [member2 …]
  • SMEMBERS:获取 set 中的所有元素。用法:SMEMBERS key
  • SISMEMBER:检查元素是否在 set 中。用法:SISMEMBER key member
  • SUNION:获取多个 set 的并集。用法:SUNION key1 [key2 …]
  • SINTER:获取多个 set 的交集。用法:SINTER key1 [key2 …]
  • SDIFF:获取两个 set 的差集(第一个 set 中有,第二个 set 中没有的元素)。用法:SDIFF key1 key2
  • SCARD:获取 set 的元素数量(即集合的基数)。用法:SCARD key
  • SPOP:从 set 中随机弹出一个元素。用法:SPOP key
  • SRANDMEMBER:从 set 中随机获取一个或多个元素。用法:SRANDMEMBER key [count]

5.5 zset类型

5.5.1 简介

zset数据类型存取有序、不允许重复、有下标,并且给每个元素赋予了一个排序权重值(score)。Redis通过权重值来给集合中的元素进行从小到大排序,权重值可以重复。一个zset类型的键最多可以存储2^32-1个元素。

其底层存储结构也用了两种,ziplist和skiplist,也有切换关系:

  • ziplist:ziplist存储元素超过128个内存超过64字节,会转为skiplist。redis7.0之前是ziplist,之后为listpack。
  • skiplist:跳跃表(之后详解)。

ps:这让我想起了之前面试官问zset类型底层是什么,当时大言不惭的说跳跃表的场景😅(当时对此深信不疑)。

5.5.2 应用场景

  • 排行榜:将用户的得分、浏览量、商品销量等排序信息存储在有序集合中,使用zadd命令添加元素,使用zrange或zrevrange命令获取排行榜的前几名或全部成员,使用zrank或zrevrank命令获取指定成员在排行榜中的排名。
  • 延迟队列:将任务及其执行时间存储在 zset 中,使用任务执行时间作为元素的分值,可以使用 zadd 命令添加任务和执行时间,使用 zrange 和 zrem 命令按执行时间获取任务。
  • 区间查询:通过 zrangebyscore 或者 zrevrangebyscore 命令根据分值范围获取元素。

5.5.3 常用命令

  • ZADD:向 zset 中添加一个或多个元素,以及它们的分值。用法:ZADD key score1 member1 [score2 member2 …]
  • ZREM:从 zset 中删除一个或多个元素。用法:ZREM key member1 [member2 …]
  • ZSCORE:获取指定元素的分值。用法:ZSCORE key member
  • ZRANK:获取指定元素在 zset 中的排名(按升序)。用法:ZRANK key member
  • ZREVRANK:获取指定元素在 zset 中的倒序排名(按降序)。用法:ZREVRANK key member
  • ZRANGE:按升序获取 zset 中的一定范围元素。用法:ZRANGE key start stop [WITHSCORES]
  • ZREVRANGE:按降序获取 zset 中的一定范围元素。用法:ZREVRANGE key start stop [WITHSCORES]
  • ZCOUNT:统计 zset 中指定分值范围内的元素数量。用法:ZCOUNT key min max
  • ZINCRBY:增减指定元素的分值。用法:ZINCRBY key increment member
  • ZCARD:获取 zset 中元素的数量(即集合的基数)。用法:ZCARD key

5.6 stream类型

5.6.1 简介

stream是Redis5.0新加的一个数据类型,通常被视为一个日志或消息队列,它是一个由多个键值对组成的可持久化、有序、可重复的数据流。一个stream类型的键最多可以存储2^64-1个键值对。

底层实现是rax tree(基数树)和listpack,rax是一种压缩的前缀树结构,消息ID是作为rax中的key,消息具体数据是使用listpack保存,并作为value和消息ID一起保存到rax tree中。

5.6.2 应用场景

  • 消息队列:生产者可以使用XADD命令将消息添加到stream中,消费者可以使用XREAD或XREADGROUP命令消费消息。
  • 实时日志:每个日志视为一个消息,可以根据时间戳、唯一的消息ID、消息内容等属性进行查询和筛选。通过XADD命令添加日志,XREAD或XREADGROUP命令获取日志。
  • 事件流处理:例如用户活动流、系统监控事件等。事件流中的每个事件被视为一个消息,可以根据事件类型、时间戳、事件属性等条件进行查询和分析。

5.6.3 常用命令

  • XADD:向 Stream 中添加一条消息。用法:XADD key [MAXLEN [~|~count] [LIMIT count]] * field1 value1 [field2 value2 …] 示例:XADD mystream * name Alice age 25
  • XLEN:获取 Stream 中的消息数量。用法:XLEN key 示例:XLEN mystream
  • XRANGE:按照 ID 范围获取 Stream 中的消息列表。用法:XRANGE key start end [COUNT count] 示例:XRANGE mystream 0-0 10
  • XREAD:读取 Stream 中的消息。用法:XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key …] id [id …] 示例:XREAD COUNT 10 STREAMS mystream 0
  • XDEL:删除 Stream 中的消息。用法:XDEL key id [id …] 示例:XDEL mystream 1578854095166-0 1578854095166-1
  • XGROUP CREATE:创建消费者组。用法:XGROUP CREATE key groupname id_or_$ [MKSTREAM] 示例:XGROUP CREATE mystream mygroup $
  • XGROUP SETID:设置消费者组的消费位置。用法:XGROUP SETID key groupname id_or_$ 示例:XGROUP SETID mystream mygroup 0-0
  • XREADGROUP:消费者组读取 Stream 中的消息。用法:XREADGROUP GROUP groupname consumerkey [COUNT count] [BLOCK milliseconds] STREAMS key [key …] id [id …] 示例:XREADGROUP GROUP mygroup consumer1 COUNT 10 STREAMS mystream >
  • XACK:消费者确认已消费的消息。用法:XACK key groupname id [id …] 示例:XACK mystream mygroup 1578854095166-0 1578854095166-1
  • XCLAIM:消费者从待处理列表中获取消息。用法:XCLAIM key groupname consumer min-idle-time ID [ID …] [IDLE ms] [TIME ms-unix-time] [RETRYCOUNT count] [FORCE] 示例:XCLAIM mystream mygroup consumer1 60000 1578854095166-0

5.7 Hyperloglog类型

5.7.1 简介

Hyperloglog是一种概率数据结构,算法的最本源则是伯努利过程。用在恒定的内存大小下估计集合的计数(不同元素的个数),以及对多个集合进行并、交运算等。优点是可以使用极少的内存空间,同时可以保证较高的准确性。每个Hyperloglog键秩序要花费12KB内存,便可计算接近2^64个不同元素的基数。

底层实现是HLL_DENSE(稠密矩阵)和HLL_SPARSE(稀疏矩阵):

  • 稀疏矩阵:计数较少时使用。

  • 稠密矩阵:计数增多,超过阈值后,会转为稠密矩阵。

5.7.2 应用场景

  • 计算网站的UV(unique visitor)数量:通过记录用户请求的 IP 地址(或浏览器 cookie 等标识符),使用 HyperLogLog 可以快速估计出网站的独立访客数。
  • 统计在线用户数量:通过记录用户登录名、客户端ID等信息,使用 HyperLogLog 可以快速估计在一段时间内在线用户的数量。
  • 统计数据库中某字段的不同取值数量:通过记录字段值,使用 HyperLogLog 可以估算不同取值的数量,例如估算某个表中年龄、地区等字段的不同取值数量。

5.7.3 常用命令

  • PFADD:向 HyperLogLog 中添加一个或多个元素。用法:PFADD key element [element …] 示例:PFADD myloglog user1 user2 user3
  • PFCOUNT:获取 HyperLogLog 的基数估计值。用法:PFCOUNT key [key …] 示例:PFCOUNT myloglog
  • PFMERGE:将多个 HyperLogLog 合并为一个。用法:PFMERGE destkey sourcekey [sourcekey …] 示例:PFMERGE mergedloglog myloglog1 myloglog2
  • PEXPIRE:设置 HyperLogLog 的过期时间。用法:PEXPIRE key milliseconds 示例:PEXPIRE myloglog 60000
  • PTTL:获取 HyperLogLog 的剩余过期时间。用法:PTTL key 示例:PTTL myloglog
  • PERSIST:移除 HyperLogLog 的过期时间。用法:PERSIST key 示例:PERSIST myloglog

5.8 GEO类型

5.8.1 简介

GEO(地理位置)是一个键值对集合,其中每个元素都包含一个经度和纬度,可以用于存储地理位置信息并支持基于位置的搜索。

它是基于zset数据类型实现的,利用geohash算法将经纬度编码为二进制字符串,并作为zset的score值。在使用GEORADIUS和GEORADIUSBYMEMBER命令搜索元素时,Redis会构建一个跳跃表,以实现高效的搜索。

5.8.2 应用场景

  • 附近的人/商家搜索:通过将用户/商家的地理位置坐标存储在 Redis 的 GEO 数据结构中,可以根据用户当前的地理位置快速查询附近的人或商家,实现定位服务和位置搜索功能。
  • 地点推荐:通过存储地点的坐标和属性,可以根据用户当前的地理位置快速推荐附近的景点、餐厅、酒店等地点,并按距离排序。
  • 打车/配送系统:可以使用 GEO 数据类型来存储司机的位置和乘客的位置,以便快速匹配附近的司机和乘客,并计算两者之间的距离。
  • 热点地理位置统计:通过记录用户地理位置的访问次数,可以统计热门地点,用于展示热门景点、餐厅等信息。

5.8.3 常用命令

  • PFADD:向 HyperLogLog 中添加元素。用法:PFADD key element [element …] 示例:PFADD myloglog a b c
  • PFCOUNT:获取 HyperLogLog 的近似基数(唯一元素数量)。用法:PFCOUNT key [key …] 示例:PFCOUNT myloglog
  • PFMERGE:将多个 HyperLogLog 合并为一个 HyperLogLog。用法:PFMERGE destkey sourcekey [sourcekey …] 示例:PFMERGE merged myloglog1 myloglog2 myloglog3
  • PEXPIRE:设置 HyperLogLog 的过期时间。用法:PEXPIRE key milliseconds 示例:PEXPIRE myloglog 60000
  • PTTL:获取 HyperLogLog 的剩余过期时间。用法:PTTL key 示例:PTTL myloglog
  • PERSIST:移除 HyperLogLog 的过期时间。用法:PERSIST key 示例:PERSIST myloglog

5.9 bitmap类型

5.9.1简介

bitmap是一种紧凑的数据结构,可以用于表示一个只有0和1的数组。位图可以用于高效地存储大规模的布尔值,以及进行位运算、位图图形化等操作。一个bitmap最多可以存储2^32-1个二进制位。

底层使用了一种“压缩位图”的数据结构。通过使用两个数组来存储位图数据:一个存储实际位的值,另一个存储每个字节中1的个数。这种方式可大大压缩位图数据的大小。

5.9.2 应用场景

  • 统计在线用户:与统计用户活跃类似,可以使用 Bitmap 来实现对在线用户的统计。比如将一个 key 对应的字符串的每个比特位表示一个用户,当某个用户在线时,将对应的比特位置为 1。此时使用 BITCOUNT 命令来计算在线的用户数。
  • Bloom Filter:利用 SETBIT 和 GETBIT命令实现快速判断一个元素是否存在于一个集合中。
  • 统计用户访问情况:可以利用 Bitmap 记录用户的访问情况,如记录用户是否已浏览一篇文章。可以使用 SETBIT 命令为特定文章的每个用户设置一个比特位,并在用户浏览过该文章时,将其对应的比特位设置为 1。这些比特位的数据将存储在同一个键值下,以记住哪个用户看过哪些文章。
  • 实现位图索引:利用 bitop 和 bitpos 命令实现对多个条件进行位运算和定位

5.9.3 常用命令

  • SETBIT:设置位图在指定偏移量的值。用法:SETBIT key offset value 示例:SETBIT mybitmap 0 1
  • GETBIT:获取位图在指定偏移量的值。用法:GETBIT key offset 示例:GETBIT mybitmap 0
  • BITCOUNT:计算位图中值为1的位的数量。用法:BITCOUNT key [start end] 示例:BITCOUNT mybitmap 0 10
  • BITOP:对多个位图执行位运算操作,并将结果保存在指定位图中。用法:BITOP operation destkey key [key …] 示例:BITOP AND result mybitmap1 mybitmap2 mybitmap3
  • BITPOS:查找位图中指定bit(0或1)第一次出现的偏移量。用法:BITPOS key bit [start] [end] 示例:BITPOS mybitmap 0 100
  • BITFIELD:使用位域操作位图。用法:BITFIELD key [GET type offset] [SET type offset value] 示例:BITFIELD mybitmap GET u4 0

End:希望对大家有所帮助,如果有纰漏或者更好的想法,请您一定不要吝啬你的赐教🙋。  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/218274.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

武林风云之linux组软raid0

小y可喜欢玩文明系列的游戏了,因为小y也一直喜欢造轮子,属于自己的轮子。 每次小y听到”要向雄鹰一样,定要遨游于天际。”感觉自己给自己打了一针强心剂,要求自己拼搏进取。 众所周知,文明是个原生的linux游戏&#xf…

助力工业产品质检,基于yolov5l集成CBAM注意力机制开发构建智能PCB电路板质检分析系统

AI助力工业质检智能生产制造已经有很多成功的实践应用了,在我们前面的系列博文中也有很多对应的实践,感兴趣的话可以自行移步阅读前面的博文即可,这里本文的核心目的就是想要基于改进的yolov5l来开发构建用于PCB电路板智能检测分析的模型&…

ssm+vue的高校智能培训管理系统分析与设计(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频: ssmvue的高校智能培训管理系统分析与设计(有报告)。Javaee项目,ssm vue前后端分离项目。 项目介绍: 采用M(model)V(view)C(controller&#xff09…

OkHttp: 拦截器和事件监听器

文章目录 1. 拦截器1. 拦截器链2. 实际案例1. 注册为应用拦截器2. 注册为网络拦截器 3. 如何选择用哪种拦截器1. 应用拦截器2. 网络层拦截器3. 重写请求4. 重写响应 4. 可用性 2. 事件监听器1. 请求的生命周期2. EventListener使用案例3. EventListener.Factory4. 调用失败的请…

办公技巧:分享五个在线画图工具,值得收藏

目录 1. processon ​编辑 2. visual paradigm online 3. zen flowchart 4. draw io 5. Excalidraw 今天小编给大家分享五个在线画图工具,感兴趣的可以下载试一试! 1. processon 说流程图除了必提http://draw.io,processon也必须要有…

大语言模型:开启自然语言处理新纪元

导言 大语言模型,如GPT-3(Generative Pre-trained Transformer 3),标志着自然语言处理领域取得的一项重大突破。本文将深入研究大语言模型的基本原理、应用领域以及对未来的影响。 1. 简介 大语言模型是基于深度学习和变压器&…

【后端学前端】第二天 css动画 动感菜单(css变量、过渡动画、过渡延迟、js动态切换菜单)

目录 1、学习信息 2、源码 3、变量 1.1 定义变量 1.2 使用变量 1.3 calc() 函数 4、定位absolute和fixed 5、transform 和 transition,动画 5.1 变形transform 5.2 transition 5.3 动画animation 6、todo 1、学习信息 视频地址:css动画 动感菜…

t-io 程序执行后,jvm不退出的原因

基于t-io 1.7.3 版本分析源码 1、设定当前时间,每10毫秒执行一次 (非守护线程) 2、对应线程池的核心线程在AioServer启动时全部激活,并且添加空任务到阻塞队列,让核心线程(非守护线程)一直存活

LeetCode:2415. 反转二叉树的奇数层(层次遍历 Java)

目录 2415. 反转二叉树的奇数层 题目描述: 实现代码与解析: BFS 原理思路: 2415. 反转二叉树的奇数层 题目描述: 给你一棵 完美 二叉树的根节点 root ,请你反转这棵树中每个 奇数 层的节点值。 例如,…

HACON error #2453:

问题:HALCON handle is NULL in operator grab_image_async" 分析:句柄为空,因为提前hv_AcqHandle.Dispose();将句柄释放掉了 解决方案:屏蔽此语句

云计算 云原生

一、引言 云计算需要终端把信息上传到服务器,服务器处理后再返回给终端。在之前人手一台手机的情况下,云计算还是能handle得过来的。但是随着物联网的发展,什么东西都要联网,那数据可就多了去了,服务器处理不过来&…

正在连接到 objects.githubusercontent.com

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 下载记录 1. 正文 下载exe文件,出现如上, 正在连接到 objects.githubusercontent.com… 2. 解决方法 2.1 Linux 如果是Linux&…

Python【Matplotlib】图例可拖动改变位置

代码: import matplotlib.pyplot as plt from matplotlib.widgets import Button# 创建一个示例图形 fig, ax plt.subplots() line, ax.plot([1, 2, 3], labelLine 1)# 添加图例 legend ax.legend(locupper right, draggableTrue)# 添加一个按钮,用于…

微服务保护--熔断降级

1.熔断降级介绍 熔断降级是解决雪崩问题的重要手段。其思路是由断路器统计服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。 断路器控制熔断和放行…

使用opencv的Laplacian算子实现图像边缘检测

1 边缘检测介绍 图像边缘检测技术是图像处理和计算机视觉等领域最基本的问题,也是经典的技术难题之一。如何快速、精确地提取图像边缘信息,一直是国内外的研究热点,同时边缘的检测也是图像处理中的一个难题。早期的经典算法包括边缘算子方法…

【Jeecg Boot 3 - 第二天】1.2、jar 包和 lib 依赖分离,部署包缩小100倍

一、场景 二、思路 三、实战 ▶ 2.1、项目 jar 包解压获取 lib config Stage 1:正常打包获取 jeecg-system-start-3.6.0.jar Stage 2:解压 获取如下文件 Stage 3:获取 lib config ▶ 2.2、获取简化版项目jar包 Stage 1&#xff1…

git checkout进行更改分支

git clone https://gitee.com/yaleguo1/minit-learning-demo.git下载代码。 cd minit-learning-demo/进入目录里边。 ls -l看一下当前分支的内容。 git checkout geek_chapter02更改分支到geek_chapter02。 ls -l看一下目录里边的内容。

微积分-三角函数2

三角函数 在上一节中,讨论了如何在直角三角形中定义三角函数,限制让我们扩展三角函数的定义域。 事实上我们可以取任意角的正弦和余弦,而不只是局限于 0 0 0~ π 2 \frac{\pi}{2} 2π​当中。 当然需要注意的是,正切函数对不是对…

Unity中URP Shader 的 SRP Batcher

文章目录 前言一、SRP Batcher是什么二、SRP Batcher的使用条件1、可编程渲染管线2、我们用URP作为例子3、URP 设置中 Use SRP Batcher开启4、使 SRP Batcher 代码路径能够渲染对象5、使着色器与 SRP Batcher 兼容: 三、不同合批之间的区别BuildIn Render Pipeline下…

八大排序(插入排序 | 选择排序 | 冒泡排序)

在我们内存中我们一般会有一些没有顺序的数据,我们成为内排序,而今天分享八大排序的是时间复杂度为O(N^2)的插入排序,选择排序和教学意义比较强的冒泡排序。 插入排序 这是插入排序的动图,通过动图我们也…