redis基础总结(数据类型)

news2024/5/20 18:17:22

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";

    @Autowired
    private 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.914402
    public 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
{
    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 模拟后台有用户点击首页,每个用户来自不同ip地址
     */
    @PostConstruct
    public 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.coloradmin.cn/o/814661.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【已解决】span的宽度与高度如何设置

本博文源于笔者基础不扎实的情况下遇到的一个问题&#xff0c;问题是我有三个span&#xff0c;想让它们宽度与高度再大点&#xff0c;结果发现怎样设置都设置不了。最后不经意间解决问题 文章目录 1、问题再现2、解决方案3、解决效果 1、问题再现 <span>1</span>…

邪恶版ChatGPT来了!

「邪恶版」ChatGPT 出现&#xff1a;每月 60 欧元&#xff0c;毫无道德限制&#xff0c;专为“网络罪犯”而生。 WormGPT 并不是一个人工智能聊天机器人&#xff0c;它的开发目的不是为了有趣地提供无脊椎动物的人工智能帮助&#xff0c;就像专注于猫科动物的CatGPT一样。相反&…

一份热乎乎的字节面试真题

一份热乎乎的字节面试真题 说说Redis为什么快 基于内存存储实现 内存读写是比在磁盘快很多的&#xff0c;Redis基于内存存储实现的数据库&#xff0c;相对于数据存在磁盘的MySQL数据库&#xff0c;省去磁盘I/O的消耗。 高效的数据结构 Mysql索引为了提高效率&#xff0c;选…

【unity】Pico VR 开发笔记(基础篇)

Pico VR 开发笔记(基础篇) XR Interaction Tooikit 版本 2.3.2 一、环境搭建 其实官方文档已经写的很详细了&#xff0c;这里只是不废话快速搭建&#xff0c;另外有一项官方说明有误的&#xff0c;补充说明一下&#xff0c;在开发工具部分说明 插件安装——安装pico的sdk和XR…

为什么定时器,串口这些东西被称之为外设

前言 &#xff08;1&#xff09;我们常常说定时器&#xff0c;串口是外设&#xff0c;但是很多人肯定有疑惑。定时器&#xff0c;串口不明明是存储在芯片里面的吗&#xff1f; &#xff08;2&#xff09;为了弄明白这个&#xff0c;就需要追溯到上个世纪了。 上个世纪的CPU与串…

【玩转Python系列【小白必看】Python多线程爬虫:下载表情包网站的图片

文章目录 前言1. 导入模块和库2. 定义函数 download_image(url, filepath)3. 定义函数 get_page()4. 主程序入口 完整代码运行效果 结束语 前言 本文主要介绍了使用Python编写的多线程爬虫程序&#xff0c;用于下载表情包网站上的图片。通过解析网页内容和使用XPath定位&#x…

Spring优雅的在事务提交/回滚前后插入业务逻辑

业务背景 业务那边想要统计下我们这边每天注册商户成功和失败的数量&#xff0c;你看看怎么给他弄下这个功能 功能实现 TransactionSynchronizationManager.registerSynchronization&#xff0c;发现这是spring事务提供的注册回调接口的方法。 在事务注解方法中&#xff0c…

Java 版 spring cloud + spring boot 工程系统管理 工程项目管理系统源码 工程项目各模块及其功能点清单

工程项目各模块及其功能点清单 一、系统管理 1、数据字典&#xff1a;实现对数据字典标签的增删改查操作 2、编码管理&#xff1a;实现对系统编码的增删改查操作 3、用户管理&#xff1a;管理和查看用户角色 4、菜单管理&#xff1a;实现对系统菜单的增删改查操…

python中有哪些比较运算符

目录 python中有哪些比较运算符 使用比较运算符需要注意什么 总结 python中有哪些比较运算符 在Python中&#xff0c;有以下比较运算符可以用于比较两个值之间的关系&#xff1a; 1. 等于 ()&#xff1a;检查两个值是否相等。 x y 2. 不等于 (!)&#xff1a;检查两个…

2024考研408-计算机网络 第一章-计算机网络体系结构学习笔记

文章目录 前言一、计算机网络概述1.1、概念及功能1.1.1、计算机网络的概念1.1.2、计算机网络的功能功能1、数据通信功能2、资源共享功能3、分布式处理功能4、提高可靠性&#xff08;分布式处理引申功能&#xff09;功能5、负载均衡&#xff08;也是分布式处理引申功能&#xff…

23 张图详解路由协议

路由的概念 在 TCP/IP 通信中&#xff0c;网络层的作用是实现终端的点对点通信。IP 协议通过 IP 地址将数据包发送给目的主机&#xff0c;能够让互联网上任何两台主机进行通信。IP 地址可以识别主机和路由器&#xff0c;路由器可以把全世界的网络连接起来。 什么是路由器 路由…

使用Flutter的image_picker插件实现设备的相册的访问和拍照

文章目录 需求描述Flutter插件image_picker的介绍使用步骤1、添加依赖2、导入 例子完整的代码效果 总结 需求描述 在应用开发时&#xff0c;我们有很多场景要使用到更换图片的功能&#xff0c;即将原本的图像替换设置成其他的图像&#xff0c;从设备的相册或相机中选择图片或拍…

【LeetCode 75】第十五题(1456)定长子串中元音的最大数目

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码运行结果&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 就难度而言&#xff0c;我觉得算不上中等&#xff0c;因为和上一题基本一致&#xff0c;只不过上一题是求最大平均数&#xff0c…

基于Django美食分享交流网站-计算机毕设 附源码10913

美食分享交流网站 摘 要 大数据时代下&#xff0c;数据呈爆炸式地增长。为了迎合信息化时代的潮流和信息化安全的要求&#xff0c;利用互联网服务于其他行业&#xff0c;促进生产&#xff0c;已经是成为一种势不可挡的趋势。在美食分享的要求下&#xff0c;开发一款整体式结构的…

为什么计算机对浮点型数字计算存在误差

我们输入的十进制小数在计算机中都是以二进制进行存储。比如&#xff1a; 我们把0.25转换为二进制 0.25 * 2 0.5 取0 0.50 * 2 1.0 取1 所以十进制0.25的二进制应当为0.01但是我们把0.3转换为二进制存储 0.3 * 2 0.6 取0 0.6 * 2 1.2 取1 0.2 * 2 0.4 取0 0.4 * …

在腾讯云服务器OpenCLoudOS系统中安装mysql(有图详解)

1. 创建MySQL安装目录 mkdir -p app/soft//mysql 2. 进入MySQL安装目录&#xff0c;下载&#xff0c;安装 cd /app/soft/mysql/ wget http://dev.mysql.com/get/mysql-5.7.26-1.el7.x86_64.rpm-bundle.tar 得到安装包&#xff1a; 解压安装包&#xff1a; 查看系统是否自带…

使用Python机器学习预测外卖送餐时间!

大家好&#xff0c;我是小F&#xff5e; 现在的天气是一天比一天热&#xff0c;好多人周末休息在家的时候&#xff0c;就会选择点外卖&#xff0c;毕竟出去一趟又晒又热。 如果你太饿了&#xff0c;点餐太晚了&#xff0c;就可能去关注外卖员送餐到哪了&#xff0c;还有多少时间…

Kotlin泛型的协变与逆变

以下内容摘自郭霖《第一行代码》第三版 泛型的协变 一个泛型类或者泛型接口中的方法&#xff0c;它的参数列表是接收数据的地方&#xff0c;因此可以称它为in位置&#xff0c;而它的返回值是输出数据的地方&#xff0c;因此可以称它为out位置。 先定义三个类&#xff1a; op…

《golang设计模式》第一部分·创建型模式-03-建造者模式(Builder)

文章目录 1. 概念1.1 角色1.2 类图 2. 代码示例2.1 设计2.2 代码2.3 类图 1. 概念 1.1 角色 Builder&#xff08;抽象建造者&#xff09;&#xff1a;给出一个抽象接口&#xff0c;以规范产品对象的各个组成成分的建造。ConcreteBuilder&#xff08;具体建造者&#xff09;&a…

移动机器人和UGV的自主导航(Matlab代码Simulink)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…