redis基础总结(数据类型)

news/2024/5/17 15:34:44

Redis十大数据类型

String

String 是redis最基本数据类型,一个key对应一个value. String类型是二进制安全的,意思是Redis的string类型可以包含任何数据,比如jpg图片或者序列化的对象;
String类型是最基本的数据类型,一个redis中字符串value最多是512M;
String类型在redis底层数据结构是SDS(简单动态字符串);
简单应用: incr可以实现 某篇文章点赞量 等功能

常用命令:

set key value  //设置指定key值
get key //获取指定key值
getrange key start end //返回key中字符串的子字符 例如: getrange  key1 0 -1; 返回value ;getrange key1 0 1 ; 返回v
getset key value //将指定key的值设置为value,并返回key的旧值;
getbit key offset //对key所存储的字符串值,获取指定偏移量上的位(bit)
mget key1 key2 key3 //获取所有(一个或多个给定key的值)
setbit key offset value//对key所储存的字符串值,设置或清楚指定偏移量上的位
setex key seconds value //将值value关联到key,并将key的过期时间设置为seconds(以s为单位)
setnx key value //只有在key不存在时 设置key的值
setrange key offset value//用value参数覆写给定key所存储的字符串值,从偏移量offset开始;
strlen key //返回key所存储的字符串值的长度
mset key value key1 value1 key2 value2. . .//同时设置一个或多个k-v对
msetnx key value[key value . . . ] //同时设置一个或多个key-value对,当且仅当所有给定key都不存在
psetex key milliseconds value // 和setex命令相似 , 但是它以毫秒为单位设置过期时间
incr key //将key中存储的数字值增1;
incrby key increment//将key所存储的值加上给定的增量值(incrment)
decr key;//将key中存储的数字值减一
decrby key decrement//将key所存储的值减去给定的减量值(decrment)
append key value//如果key已经存在并且是一个字符串,append命令将value追加到key原来的值的末尾

SDS

Redis底层是用C来实现的,C语言中没有java里面的string类型,只能靠自身的cha[]来实现,字符串在C语言中的存储方式,想要获取[redis]的长度,需要从头开始遍历,直到遇到'\0' 为止,所以,redis 没有直接使用C语言的字符串标识,而是自己构建了一种名为简单动态字符串SDS(simple dynamic string)的抽象类型,并将SDS作为Redis的默认字符串;

struct _attribute_((_packed_)) sdshdr8{uint8_t len;unit8_t alloc;unsigned char flags;char buf[];
};
struct _attribute_((_packed_)) sdshdr16{uint16_t len;unit16_t alloc;unsigned char flags;char buf[];
};

sds与char对比:

SDS类型RedisObject内部对应3大物理编码:

int: 保存long类型的64位有符号整数,最小值-2^63; 最大值是2^63-1; 默认值是0L; 最多19位数字;另外 只有整数才会使用int,如果是浮点数,Redis内部其实先将浮点数转化为字符串值,然后在保存;
当执行 set k1 123 时:
当字符串键值内容可以有那个一个64位有符号整形来表示时,Redis会将键值转化为long类型进行存储,此时即对应OBJ_ENCODING_INT 编码类型; Redis启动会预先建立1W个分别存储0到9999的redisObject变量作为共享对象,折旧意味着如果set字符串的键值在0~10000之间的化,则可以直接使用共享对象,而不需要在建立新对象,此时键值不占空间;

embstr(embedded string):嵌入式的字符串; 代表embstr格式的sds(simple dynamic string 简单动态字符串),保存长度小于44字节的字符串;
当执行set k1 abc 时:
对于长度小于44的字符串,Redi对键值采用OBJ_EnCODING_EMBSTR方式,从内存结构上讲,即字符串sds结构体与对应的Redisobject对象分配在同一块连续的内存空间;
raw:长度超过44字节的字符串
当字符串长度大于44时,redis则会将键值的内部编码方式改为OBJ_ENCODING_RAW格式,这与embstr编码方式不同之处在于,此时动态字符串sds的内存与依赖的redisObject的内存不连续了;
另外,对于embstr,由于其实现是只读的,如果对其进行修改,都是先转为raw在进行修改,因此,只要是修改embstr对象,修改后的对象一定是raw的,无论格式是否到了44字节;判断不出来,就取最大Raw

获取编码格式命令:object encoding key

总结:

List

Redis list 是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边).它的底层实际是个双端链表,最多可以包含 2^32 - 1 个元素 (4294967295, 每个列表超过40亿个元素). 主要功能有push/pop等,一般用在栈、队列、消息队列等场景。left、right都可以插入添加;如果键不存在,创建新的链表;如果键已存在,新增内容;如果值全移除,对应的键也就消失了。

常用命令:

blpop key1 [key2] timeout//移出并获取列表的第一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
brpop key1 [key2] timeout//移出并获取列表的最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
lpush/rpush/lrange //在左/右/指定位置  新增元素
lpop/rpop //弹出最左 /最后侧一个元素
lindex key index //按照索引下标获得元素(从上到下) ( lindex  list2 3  获取list2  下标是3的元素)
llen //获取列表中元素的个数
lrem key  n v1 ; //删除列表中N个值等于v1 的元素
ltrim key 开始index  结束index;//截取指定范围的值后再赋值给key
rpoplpush list2 list 3 //移除list2列表的最后一个元素,并将该元素加到list3列表 并返回
lset key index value// 修改列表指定index的值 为value
linsert key before/after 已有值  插入的新值;//在list某个已有值的前后再添加具体值(linsert list3 before oracle mysql  在list3中 oracle前插入mysql)

简单应用: 关注多个公众号 文章推送(例如: 公众号 1 和公众号2 发布了两篇文章 分别是11和22; 账号A关注了这两个公众号,只要他们发布新文章,就会安装进A的list :lpush likearticle:A-id 11 12 ; A 查看自己订阅号的全部文章,类似于分页 :lrange likearticle:A-id 0 9)

Redis List 底层数据结构

在Redis3.0之前,list采用的底层数据结构是ziplist(压缩列表)+linkedList(双向链表)
然而在高版本的redis中底层数据结构是quicklist(替换了ziplist+linkedList),而quicklist也用到了ziplist;quicklist就是一个链表,而链表中的每个元素又是一个压缩列表;
比较早版本的redis中,list底层两种实现:
1.当列表对象中元素的长度比较小或者数量比较少的时候,采用ziplist存储
2.当列表中元素的长度比较大或者数量比较多 则会转而使用linkedlist存储;
优缺点:
1.ziplist的优点是内存紧凑,访问效率高,缺点是更新效率低,并且数据量较大时,可能导致大量内存复制;
2.linkedlist的优点是 节点修改效率高,但是需要额外的内存开销,并且节点较多时,会产生大量内存碎片;
综上所述,在redis3.2之后,list的底层实现变为快速列表 quicklist;

quicklist

redis6中, quicklist实际上是ziplist和linkedList的混合体,它将linkedlist按段切分,每一段用ziplist来紧凑存储,多个ziplist之间使用双向指针串接起来,*zl指向一个ziplist,一个ziplist可以存放多个元素.
简单来说,一个 quicklist 就是一个链表,而链表中的每个元素又是一个 ziplist。这种设计减少了数据插入时内存空间的重新分配,以及内存数据的拷贝。同时,quicklist 限制了每个节点上 ziplist 的大小,一旦一个 ziplist 过大,就会采用新增 quicklist 节点的方法。
不过,又因为 quicklist 使用 quicklistNode 结构指向每个 ziplist,无疑增加了内存开销。为了减少内存开销,并进一步避免 ziplist 连锁更新问题,Redis 在 5.0 版本中,就设计实现了 listpack 结构。listpack 结构沿用了 ziplist 紧凑型的内存布局,把每个元素都紧挨着放置。

redis7中用listpack代替了ziplist ; 所以,在redis7中 quicklist是listpack和linkedlist的结合体;

ziplist和listpack

ziplist

ziplist结构如下:

privious_entry_length,encoding长度都可以根据编码方式推算,真正变化的是content,而content长度记录在encoding里 ,因此entry的长度就知道了。entry总长度 = privious_entry_length字节数+encoding字节数+content字节数.
链表在内存中,一般是不连续的,遍历相对比较慢,而ziplist可以很好的解决这个问题。如果知道了当前的起始地址,因为entry是连续的,entry后一定是另一个entry,想知道下一个entry的地址,只要将当前的起始地址加上当前entry总长度。如果还想遍历下一个entry,只要继续同样的操作。
从 ziplist 的设计不足出发,ziplist 的最大特点,就是它被设计成一种内存紧凑型的数据结构,占用一块连续的内存空间,以达到节省内存的目的。但是,在计算机系统中,任何一个设计都是有利有弊的。对于 ziplist 来说,这个道理同样成立。虽然 ziplist 节省了内存开销,可它也存在两个设计代价:一是不能保存过多的元素,否则访问性能会降低;二是不能保存过大的元素,否则容易导致内存重新分配,甚至可能引发连锁更新的问题。所谓的连锁更新,简单来说,就是 ziplist 中的每一项都要被重新分配内存空间,造成 ziplist 的性能降低.
连锁更新:
压缩列表每个节点正因为需要保存前一个节点的长度字段,就会有连锁更新的隐患
第一步:现在假设一个压缩列表中有多个连续的、长度在 250~253 之间的节点,如下图:

因为这些节点长度值小于 254 字节,所以 prevlen 属性需要用 1 字节的空间来保存这个长度值.
第二步:这时,如果将一个长度大于等于 254 字节的新节点加入到压缩列表的表头节点,即新节点将成为entry1的前置节点,如下图:


因为entry1节点的prevlen属性只有1个字节大小,无法保存新节点的长度,此时就需要对压缩列表的空间重分配操作并将entry1节点的prevlen 属性从原来的 1 字节大小扩展为 5 字节大小。
第三步:连续更新问题出现


entry1节点原本的长度在250~253之间,因为刚才的扩展空间,此时entry1节点的长度就大于等于254,因此原本entry2节点保存entry1节点的 prevlen属性也必须从1字节扩展至5字节大小。entry1节点影响entry2节点,entry2节点影响entry3节点......一直持续到结尾。这种在特殊情况下产生的连续多次空间扩展操作就叫做「连锁更新」

listpack

listpack 是 Redis 设计用来取代掉 ziplist 的数据结构,它通过每个节点记录自己的长度且放在节点的尾部,来彻底解决掉了 ziplist 存在的连锁更新的问题.
listpack由四部分组成,如下图:

total bytes: 为整个listpack的空间大小,占用4个字节,每个listpack最多占用4294967295Bytes(2^32-1).
number-elements: listpack中的元素个数,即Entry的个数占用2个字节
element-1~element-N : 具体的元素
listpack-end-byte:为listpack结束标志,占用一个字节,内容为0xff


总结:
zipList为了节省内存 采用了紧凑的连续存储,ziplist是一个双向链表,可以再时间复杂度为O(1)下从头部或者尾部进行pop或push; 缺点是:新增或更新元素可能会出现连锁更新现象,不能保存过多的元素否则查询效率会降低,数量小核内容小的情况下可以使用;
和ziplist列表类似,listpack列表项也包含了元数据信息和数据本身,不过为了避免ziplist引起连锁更新问题,listpack中每个列表项不在像ziplist列表项那样保存其前一个列表的长度.

Hash

Redis hash 是一个 string 类型的 field(字段) 和 value(值) 的映射表,hash 特别适合用于存储对象。Redis 中每个 hash 可以存储 2^32 - 1 键值对(40多亿)
常用命令:

hset/hget/hmset/hmget/hgetall/hdel //插入/获取/批量插入/批量获取/获取全部/删除
hlen //后续某个key内的全部数量
hexits key filed //key里面是否存在filed的值
hkeys/hvals //hkeys hash2 (返回hash2下所有filed), hvals hash2(返回hash2下所有value)
hincrby/hincrbyfloat key file number//指定key filed 增加number  浮点数用float
hsetnx  key file value // 不存在赋值返回1; 存在无效返回0;

简单应用场景: 简单购物车设计

购物车新增商品 - hset shopcar:uid1024 334488 1
新增商品一 hset shopcar:uid1024 334477 1
增加商品数量 一 hincrby shopcar:uid1024 3344771
商品总数 一 hlen shopcar:uid 1024
全部选择一 hgetall shopcar:uid1024

redis6中hash底层是ziplist+hashtable; redis7中 用listpack替代了ziplist;
默认情况向 hash对象保存的键值对数量小宇512个,并且所有键值对的键和值的字符串长度都小宇64byte时用ziplist(redis7用listpack),反之用hashtable; ziplist/listpack 可以升级到hashtable,但是不可以反过来降级; 一旦从ziplist/listpack升级到hashtable ,hash类型就会一直使用hashtable进行保存,而不能转回去; 在节省内存空间方面hashtable没有ziplist/listpack 高效.
这两个参数也可以通过设置来改变:
hash-max-ziplist-entries/hash-max-listpack-entries: 使用ziplist/listpack 保存时哈希集合中的最大元素个数;
hash-max-ziplist-value/hash-max-listpack-value:使用ziplist/listpack 保存时哈希集合中单个元素的最大长度

Set

Redis的set是String类型的无序集合.集合成员是唯一的,这就意味着集合中不能出现重复的数据,集合对象的编码可以是intset或者hashtable.
redis中set集合是通过hashtable实现的,所以添加,删除,查找的复杂度 都是O(1);集合中最大成员数为2^32-1(每个集合可存储40多亿个成员);
常用命令:

sadd key member[member ...] //新增
smembers key //遍历集合中的所有元素
sismember key member //判断元素是否存在集合中
srem key member [member ...] //删除集合中元素
scard //scard set1  获取set1集合中元素个数
srandmember key [数字N] //从集合中随机展现N个元素,元素不删除 如果超过最大值,则全部取出
spop key [数字N] //从集合中随机弹出元素,出一个删一个
smove key1 key2 v1;//将key1 中的v1 赋值给key2
//========= 集合运算  A - abc12   B-123ax
sidff key [key...] // 属于A但不属于B的元素构成的集合
sunion key [key...] //属于A或者属于B的元素合并后的集合
sinter key [key...] //属于A同时也属于B的共同拥有的元素构成的集合
sintercard numkeys key [key...] [limit n] //numkeys 表示几个去重的集合 命令如 sintercard 2 key1 key2 --返回key1 和key2 两个集合交集的元素个数; sintercard 3 key1 key2 key3 limit  4 --返回key 1 2 3 交集元素个数 如果超过4 则返回4  否则返回真实个数

简单应用:

//=====微信抽奖小程序:
sadd key userID//新增用户ID参与抽奖 
scard key//显示已经有多少人参与活动
//抽取N个人中奖  : 
srandmember key N //不删除抽取 ;   
spop key N //删除抽取
//=======微信朋友圈点赞查看同赞朋友:
sadd pub:msgID  userID1 userID2//user1 user2点赞
srem pub:msgID userID2 //user2取消点赞
smembers pub:msgID //展现点赞的用户
scard pub:msgID //获取点赞的用户数
sismember pub:msgID userID3//判断user3 是否点过赞

Redis用intset或者hashTable存储set,如果元素都是整数类型,就用intset存储.如果不是整数类型,就用hashtable(数组+链表的结构来存储).key就是元素的值,value为null
集合元素都是long类型,并且元素个数<=set-max-intset-entries 编码就是intset,反之就是hashtable;

ZSet

Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数,redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复,zset集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。 集合中最大的成员数为 2^32 - 1;
常用命令:

zadd key score member [score member] //添加元素
zrange key start stop [withscores] //按照元素分数从小到大的顺序返回索引从start到stop之间的所有元素
zrevrange  key start stop [withscores] //按照元素分数从大到小的顺序返回索引从start到stop之间的所有元素
zrangebyscore key min max [withscores] [limit offset count] //获取指定分数范围的元素
(是不包含的意思;  zrangebyscore zset1 (60 90 withscores (返回60到90之间的key和value 不包含60)  
zscore key member //获取元素分数 
zcard zset1//获取zset1元素个数
zcount zset1 60 90 //获取分数区间内元素个数
zrank // 获取value在zset中的下标位置  zrank zset1 v2   (获取zset1中v2的下标)
zscore zset1 v1 // 按照值获取对应分数
zrem key 某score下对应的value值//删除 
zincrby key increment member //增加某个元素的分数
zcount key min max //获取指定分数范围内的元素个数
zmpop //从键名列表中的第一个非空排序集中弹出一个或多个元素,他们是成员分数对(key value score)
zrank key values //获取下标值
zrevrank key values //逆序获取下标值

应用场景:

//=====根据商品销售对商品进行排序显示
//===定义商品销售排行榜(sorted set集合),key为goods:sellsort,分数为商品销售数量
zadd goods:sellsort 9 1001 15 1002 // 商品编号1001 的销量是9,编号1002的销量是15
zincrby goods:sellsort 2 1001 //有一个客户买了2件商品1001,商品编号1001销量加2
zrange goods:sellsort 0 9 withscores //求商品销量前10名

当有序集合中包含的元素数量超过服务器属性 server.zset_max_ziplist_entries 的值(默认值为 128 ),或者有序集合中新添加元素的 member 的长度大于服务器属性server.zset_max_ziplist_value 的值(默认值为 64 )时,redis会使用跳跃表作为有序集合的底层实现。否则会使用ziplist作为有序集合的底层实现(redis7用listpack实现)

跳表

对于一个单链表来讲,即便链表中存储的数据是有序的,如果我们要想在其中查找某个数据,也只能从头到尾遍历链表。这样查找效率就会很低,时间复杂度会很高O(N)

对单链表进行升维,也叫空间换时间.如下:


从这个例子里,我们看出,加来一层索引之后,查找一个结点需要遍历的结点个数减少了,也就是说查找效率提高了.
skiplist 是一种以空间换取时间的结构.由于链表无法进行二分查找,因此借鉴数据库索引的思想,提取出链表中关键节点(索引),先在关键节点上查找,在进入下层链表查找,提取多层关键节点,就形成了skiplist. 但是 由于索引也需要占据一定空间,所以,索引添加的越多,空间占用的越多

总结:
跳表=链表+多级索引
跳表的时间复杂度O(logn) ;空间复杂度O(N)
优点:跳表是一个最典型的空间换时间解决方案,而且只有在数据量较大的情况下才能体现出来优势。而且应该是读多写少的情况下才能使用,所以它的适用范围应该还是比较有限的
缺点:维护成本相对要高,在单链表中,一旦定位好要插入的位置,插入结点的时间复杂度是很低的,就是O(1) ,但是,新增或者删除时需要把所有索引都更新一遍,为了保证原始链表中数据的有序性,我们需要先找到要动作的位置,这个查找操作就会比较耗时最后在新增和删除的过程中的更新,时间复杂度也是O(log n)

GEO

Redis GEO 主要用于存储地理位置信息,并对存储的信息进行操作,包括:添加地理位置的坐标。获取地理位置的坐标。计算两个位置之间的距离。根据用户给定的经纬度坐标来获取指定范围内的地理位置集合
常用命令

geoadd key longitude latitude member [longitude latitude member ...]//添加经纬度坐标
geopos key member [member ...]//从给定的key里返回所有的指定名称(member)的位置,不存在的返回null
geohash key member [member...] //获取一个或多个元素的geohash值
geodist key member1 member2 [m|km|ft|mi] // 用于返回两个给定位置之间的距离
georadius  //以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。
GEORADIUS city 116.418017 39.914402 10 km withdist withcoord count 10 withhash desc //WITHDIST: 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。WITHCOORD: 将位置元素的经度和维度也一并返回。WITHHASH: 以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试, 实际中的作用并不大 ; COUNT 限定返回的记录数。
georadiusbymember city 天安门 10 km withdist withcoord count 10 withhash//找出位于指定范围内的元素,中心点是由给定的位置元素決定

简单应用:

//=========地图附近的酒店推送
@Service
@Slf4j
public class GeoService
{public static final String CITY ="city";@Autowiredprivate RedisTemplate redisTemplate;
//新增地点public String geoAdd(){Map<String, Point> map= new HashMap<>();map.put("天安门",new Point(116.403963,39.915119));map.put("故宫",new Point(116.403414 ,39.924091));map.put("长城" ,new Point(116.024067,40.362639));redisTemplate.opsForGeo().add(CITY,map);return map.toString();}//获取经纬度坐标public Point position(String member) {List<Point> list= this.redisTemplate.opsForGeo().position(CITY,member);return list.get(0);}//geohash算法生成的base32编码值public String hash(String member) {List<String> list= this.redisTemplate.opsForGeo().hash(CITY,member);return list.get(0);}//获取两个给定位置之间的距离public Distance distance(String member1, String member2) {Distance distance= this.redisTemplate.opsForGeo().distance(CITY,member1,member2, RedisGeoCommands.DistanceUnit.KILOMETERS);return distance;}
//通过经度,纬度查找附近的,北京王府井位置116.418017,39.914402public GeoResults radiusByxy() {Circle circle = new Circle(116.418017, 39.914402, Metrics.KILOMETERS.getMultiplier());//返回50条RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending().limit(50);GeoResults<RedisGeoCommands.GeoLocation<String>> geoResults= this.redisTemplate.opsForGeo().radius(CITY,circle, args);return geoResults;}public GeoResults radiusByMember() {//通过地方查找附近String member="天安门";//返回50条RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending().limit(50);//半径10公里内Distance distance=new Distance(10, Metrics.KILOMETERS);GeoResults<RedisGeoCommands.GeoLocation<String>> geoResults= this.redisTemplate.opsForGeo().radius(CITY,member, distance,args);return geoResults;}
}

HyperLogLog

HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定且是很小的。
在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。
但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素
Redis使用了214=16384个桶,误差为0.81%,精度相当高。Redis使用一个long型哈希值的前14个比特用来确定桶编号,剩下的50个比特用来做基数估计。而2^6=64,所以只需要用6个比特表示下标值,在一般情况下,
一个HLL数据结构占用内存的大小为16384*6/8=12kB,Redis将这种情况称为密集(dense)存储。
常用命令:

pfadd key element [element...] //添加指定元素到hyperloglog中
pfcount key [key...] 返回给定hyperloglog 的技术估算值
pfmerge destkey sourcekey [sourcekey...]//将多个hyperloglog合并为一个hyperloglog

demo

//=====获取去重后某个页面的访问量====
//=========
@Service
@Slf4j
public class HyperLogLogService
{@Resourceprivate RedisTemplate redisTemplate;/*** 模拟后台有用户点击首页,每个用户来自不同ip地址*/@PostConstructpublic void init(){log.info("------模拟后台有用户点击首页,每个用户来自不同ip地址");new Thread(() -> {String ip = null;for (int i = 1; i <=200; i++) {Random r = new Random();ip = r.nextInt(256) + "." + r.nextInt(256) + "." + r.nextInt(256) + "." + r.nextInt(256);Long hll = redisTemplate.opsForHyperLogLog().add("hll", ip);log.info("ip={},该ip地址访问首页的次数={}",ip,hll);//暂停几秒钟线程try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }}},"t1").start();}@ApiOperation("获得IP去重后的首页访问量")@RequestMapping(value = "/uv",method = RequestMethod.GET)public long uv(){return redisTemplate.opsForHyperLogLog().size("hll");}
}

bigmap

由0和1状态表现的二进制位的bit数组


说明:用String类型作为底层数据结构实现的一种统计二值状态的数据类型.位图本质是数组,它是基于String数据类型的按位的操作。该数组由多个二进制位组成,每个二进制位都对应一个偏移量(我们称之为一个索引)。
Bitmap支持的最大位数是232位,它可以极大的节约存储空间,使用512M内存就可以存储多达42.9亿的字节信息(232 = 4294967296)
常用命令:

setbit key offset value //设置 setbit zhangsan 1 1 ; setbit zhangsan 4 1; 设置张三1号 4号打卡  
getbit key offset //
strlen key //获取占用的字节,超过8位后自己按照8位一组-byte在扩容
bitcount key start end //返回指定key中[start end]中为1的数量   bitcount zhangsan  ; 返回2 
bitop operation destkey key//对不同的二进制存储数据进行位运算(AND、OR、NOT、XOR)   (bittop and destkey 20200808 20200809  返回连续两天签到的bigmap ; bitcount destkey  返回2 )

bitfield

通过bitfield命令可以一次性操作多个比特位域(指的是连续的多个比特位),它会执行一系列操作并返回一个响应数组,这个数组中的元素对应参数列表中的相应操作的执行结果。说白了就是通过bitfield命令我们可以一次性对多个比特位域进行操作。

Stream

Redis Stream 是 Redis 5.0 版本新增加的数据结构。
Redis Stream 主要用于消息队列(MQ,Message Queue),Redis 本身是有一个 Redis 发布订阅 (pub/sub) 来实现消息队列的功能,但它有个缺点就是消息无法持久化,如果出现网络断开、Redis 宕机等,消息就会被丢弃。
简单来说发布订阅 (pub/sub) 可以分发消息,但无法记录历史消息。

参考文档:https://blog.csdn.net/weixin_45735834/article/details/126559660


http://www.mrgr.cn/p/77027525

相关文章

力扣天天练--week3-LeetCode75

topic75-9-t443:压缩字符串 题目描述&#xff1a; 给你一个字符数组 chars &#xff0c;请使用下述算法压缩&#xff1a; 从一个空字符串 s 开始。对于 chars 中的每组 连续重复字符 &#xff1a; 如果这一组长度为 1 &#xff0c;则将字符追加到 s 中。 否则&#xff0c;需…

企业知识文档管理+群晖nas安全云存储

企业知识管理系统&#xff0c;利用软件系统或其他工具的企业管理方法&#xff0c;利用软件系统或其他工具&#xff0c;对组织中大量的有价值的方案、策划、成果、经验等知识进行分类存储和管理&#xff0c;积累知识资产避免流失&#xff0c;促进知识的学习、共享、培训、再利用…

C++ ——STL容器【list】模拟实现

代码仓库&#xff1a; list模拟实现 list源码 数据结构——双向链表 文章目录 &#x1f347;1. 节点结构体&#x1f348;2. list成员&#x1f349;3. 迭代器模板&#x1f34a;4. 迭代器&#x1f34b;5. 插入删除操作&#x1f34c;5.1 insert & erase&#x1f34c;5.2 push_…

【C++】开源:跨平台轻量日志库easyloggingpp

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍跨平台轻量日志库easyloggingpp。 无专精则不能成&#xff0c;无涉猎则不能通。。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&am…

论文笔记--GloVe: Global Vectors for Word Representation

论文笔记--GloVe: Global Vectors for Word Representation 1. 文章简介2. 文章概括3 文章重点技术3.1 两种常用的单词向量训练方法3.2 GloVe3.3 模型的复杂度 4. 文章亮点5. 原文传送门6. References 1. 文章简介 标题&#xff1a;GloVe: Global Vectors for Word Representa…

<MySQL> Centos 7环境安装MySQL

Centos 7环境安装MySQL 1.卸载不要的环境 停止MySQL服务 systemctl stop mariadb.service systemctl stop mysqld禁止MySQL服务开机自启 systemctl disable mysqld卸载MySQL软件包 yum remove mysql-server mysql-client删除MySQL数据目录 rm -rf /var/lib/mysql清理MySQ…

安装了pyintaller后出现:‘pyinstaller‘ 不是内部或外部命令,也不是可运行的程序或批处理文件。

2023年7月31日&#xff0c;周一上午 我昨天晚上也遇到了这个问题&#xff0c;后来解决了 目录 出错原因解决方法怎么找到Scripts文件夹 出错原因 出现这个错误是因为你没给python的Scripts文件夹添加环境变量&#xff0c; Scripts存放着pip安装包时产生的可执行文件。 解决…

CentOS下 Docker、Docker Compose 的安装教程

Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#xff0c;也可以实现虚拟化。容器是完全使用沙箱机制&#xff0c;相互之间不会有任何接口。 Docker Compose是用于定义…

如何启用路由器dhcp?快解析如何内网穿透?

一、什么是DHCP&#xff1f; 动态主机设置协议&#xff08;DHCP&#xff09;是一种使网络管理员能够集中管理和自动分配 IP 网络地址的通信协议。在网络中&#xff0c;每个联网设备都需要分配独有的 IP 地址。并当有新计算机移到网络中的其它位置时&#xff0c;能自动收到新的…

百度文心一言接入教程-Java版

原文链接 前言 前段时间由于种种原因我的AI BOT网站停运了数天&#xff0c;后来申请了百度的文心一言和阿里的通义千问开放接口&#xff0c;文心一言的接口很快就通过了&#xff0c;但是文心一言至今杳无音讯。文心一言通过审之后&#xff0c;很快将AI BOT的AI能力接入了文心…

从头学前端-CSS3提升-续

CSS3 2D转换 关键字&#xff1a;transform 移动&#xff1a;沿着x,y轴移动&#xff0c;不会影响盒子的位置&#xff0c;对行内元素没有效果 div {width: 100px;height: 100px;background-color: rebeccapurple;transform: translate(100px,100px);transform: translateX(100p…

TSINGSEE青犀视频安防监控视频平台EasyCVR新增密码复杂度提示

智能视频监控平台TSINGSEE青犀视频EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等&#xff0c;能对外分发RTSP、RTM…

孩子近视有必要用全光谱灯吗?全光谱led灯推荐

当然&#xff0c;有必要!全光谱LED灯的光源分布更加均匀&#xff0c;使空间更加美观舒适&#xff0c;而普通灯的光源分布可能会在一定范围内分布不均匀。全光谱它的使用寿命长达20-30万小时&#xff0c;而普通灯的使用寿命仅为1000-2000小时&#xff0c;因此在长期使用上&#…

kettle开发-Day40-AI分流之case/switch

前言&#xff1a; 前面我们讲到了很多关于数据流的AI方面的介绍&#xff0c;包括自定义组件和算力提升这块的&#xff0c;今天我们来学习一个关于kettle数据分流处理非常重要的组件Switch / Case 。当我们的数据来源于类似日志、csv文件等半结构化数据时&#xff0c;我们需要在…

React之组件的生命周期

React之组件的生命周期 一、概述二、整体说明三、挂载阶段四、更新阶段五、卸载阶段 一、概述 生命周期:一个事务从创建到最后消亡经历的整个过程组件的生命周期&#xff1a;组件从被创建到挂载到页面中运行&#xff0c;再到组件不用时卸载的过程意义&#xff1a;理解组件的生…

Windows环境下git客户端中的git-bash和MinGW64

我们在 Windows10 操作系统下&#xff0c;安装了 git 客户端之后&#xff0c;可以通过 git-bash.exe 打开一个 shell&#xff1a; 执行一些 linux 系统里的命令&#xff1a; 注意到上图紫色的 MINGW64. Mingw-w64 是原始 mingw.org 项目的改进版&#xff0c;旨在支持 Window…

数据安全

数据的备份与恢复 1. 数据备份技术 任何数据在长期使用过程中&#xff0c;都存在一定的安全隐患。由于认为操作失误或系统故障&#xff0c;例如认为错误、程序出错、计算机失效、灾难和偷窃&#xff0c;经常造成数据丢失&#xff0c;给个人和企业造成灾难性的影响。在这种情况…

公网访问的Linux CentOS本地Web站点搭建指南

文章目录 前言1. 本地搭建web站点2. 测试局域网访问3. 公开本地web网站3.1 安装cpolar内网穿透3.2 创建http隧道&#xff0c;指向本地80端口3.3 配置后台服务 4. 配置固定二级子域名5. 测试使用固定二级子域名访问本地web站点 前言 在web项目中,部署的web站点需要被外部访问,则…

TypeScript -- 函数

文章目录 TypeScript -- 函数JS -- 函数的两种表现形式函数声明函数的表达式es6 箭头函数 TS -- 定义一个函数TS -- 函数声明使用接口(定义)ts 定义参数可选参数写法 -- ?的使用TS函数 -- 设置剩余参数函数重载 TypeScript – 函数 JS – 函数的两种表现形式 我们熟知js有两…