Redis学习与入门----深度历险(一)

2023-11-21 20:30

本文主要是介绍Redis学习与入门----深度历险(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

      • redis操作数据的基础数据结构
        • string
        • list
        • hash
        • set
        • zset(跳跃链表)
      • redis分布式锁
        • 分布式锁的使用
        • 分布式锁的超时问题
      • redis的简单延时队列
      • redis的位图数据结构
        • bitcount和bitpos
      • HyperLogLog(高级数据结构)
      • 布隆过滤器
        • 布隆过滤器是什么
        • 布隆过滤器的原理
        • 布隆过滤器一些的常用场景

redis操作数据的基础数据结构

通俗易懂的Redis数据结构基础教程

string

string是键值对的形式。Redis是将值存在内存中的,其为了避免内存的频繁分配,提供了预分配内存空间的方法。当字符串是少于1M的时候,是两倍扩容的方式。当内存大于1M时,采用+1M的方式扩容。字符串的最大可用内存为512M。

set 变量名 值
get 变量名
获取子串:getrange 变量名 start end
覆盖子串:setrange 变量名 start end
在最后追加子串:append 变量名 子串值
用作计数器:1.incrby 变量名 加数2.decrby 变量名 减数3.不写默认增加或减少14.计数器是有范围的,它不能超过Long.Max,不能低于Long.MIN
设置过期时间:expire 变量名 时间(默认秒为单位)
查看剩余生命:ttl 变量名 
删除变量:del 变量名
list

list结构,即链表结构,在redis中,提供的是双向链表。结合使用rpush/rpop/lpush/lpop四条指令,你可以将其作为队列形式,也可以只采用一端,形成栈的结构。

分为左和右:
rpush 链表名 值 值 值 ...
rpop 链表名
lpush 链表名 值 值 值 ...
lpop 链表名
计算链表长度: llen 链表名
通过下标访问链表:lindex 链表名 下标(从0开始)
通过lrange访问链表子串: lrange 链表名 start end
访问整个链表的方法:1.通过llen计算得到链表长度,lrange 链表名 0 链表长度-12.lrange 链表名 0 -1
更改指定链表下标的值:lset 链表名 下标 新值
在指定值前面或者后面插入值:linsert 链表名 before/after 指定值 插入值
删除指定值的元素:lrem 链表名 数量 删除值
定长链表的一个使用: 1.第一步:ltrim 链表名 起始下标 结束下标2.第二步:lrange 链表名 0 -1
hash

类似于JDK1.7的hashmap结构。以链表+数组的形式存储数据。

hset hash表名 key value
hmset hash表名 key value key value ....
hget hash表名 key
hmget hash表名 key key key ...
获取hash表中所有的键值对:hgetall hash表名
获取hash表中所有的key:hkeys hash表名
获取hash表中所有的value:hvals hash表名
删除key:hdel hash表名 key
判断key是否存在:hexits hash表名 key
hash也可以作为计数器来使用,只要每一个的值是整数:1.hincrby hash表名 key value(整数)2.如果value不是整数,使用hincrby会报错。

同hashmap一样,rehash是非常耗时的操作,所以redis的hash结构,采用的是渐进式的rehash。
rehash的时候,会保存新旧两个hash结构,查询的时候,同时查询两个结构,并在后面命令中,慢慢将旧hash中的值,传给新hash。当旧hash最后一个元素移除后,就删除该数据结构。
hash结构可以用来存储用户信息。不同于整个对象都要序列化的字符串,hash可以对用户的每个字段进行单独存储。减少了流量的浪费,但存储消耗高于字符串

set

Java程序员都知道HashSet的内部实现使用的是HashMap,只不过所有的value都指向同一个对象。Redis的set结构也是一样,它的内部也使用hash结构,所有的value都指向同一个内部值。

sadd 表名 元素 元素 元素 ...
列出集合元素: smembers 表名 
获取集合长度: scard 表名
获取随机元素: srandmember 表名
删除一个或多个指定值的元素:hrem 表名 元素 元素 ...
随机删除一个元素:spop 表名 
判断元素是否存在:sismember 表名 元素 
zset(跳跃链表)

SortedSet(zset)是Redis提供的一个非常特别的数据结构,一方面它等价于Java的数据结构Map<String, Double>,可以给每一个元素value赋予一个权重score,另一方面它又类似于TreeSet,内部的元素会按照权重score进行排序,可以得到每个元素的名次,还可以通过score的范围来获取元素的列表。

zset底层实现使用了两个数据结构,第一个是hash,第二个是跳跃列表,hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到相应的score值。跳跃列表的目的在于给元素value排序,根据score的范围获取元素列表

zadd 表名 score value score value ...
查看元素的权重:zscore 表名 value
正向查看元素,由小到大对元素进行排序(最小的返回0):zrank 表名 value
反向查看元素,由大到小对元素进行排序(最大的返回0):zrevrank 表名 value 
根据score范围获取其中的元素列表:zrangebyscore 表名 起始值 结尾值
带有score的返回:zrangebyscore 表名 起始值 结尾值 withscores
返回整个表的,由小到大:zrangebyscore 表名 -inf +inf withscores 
返回整个表的,由大到小:zrangebyscore 表名 +inf -inf withscores
移除范围内的元素:zremrangebyscore 表名 起始score 结束score

在这里插入图片描述

redis分布式锁

分布式应用在逻辑处理时经常会遇到并发问题。比如要修改内存中的一个对象的状态。那么步骤肯定大致分为三步,先读出该对象状态,再修改该对象状态,再将新状态写回内存。因为这样的操作不是原子操作,在并发情况下是很容易出现问题的。所以可以考虑使用分布式锁来限制程序的并发执行。

分布式锁的使用
  • 最开始分布式锁采用setnx(set if not exists)的方法去锁住一个对象操作,只允许一个客户端(即一个线程)来操作。
  • 后面发现如果在客户端获取到锁之后,却在中间发生了异常,导致锁没有释放,那么就会陷入死锁。所以我们可以在调用setnx后,再执行expire方法,对这个锁加上一个过期时间。但是如果异常发生在了setnx和expire之间,仍然会产生死锁,因为这两个操作并不是一个原子操作。
  • 在redis2.8之后,redis中加入了set的扩展参数nx和ex。使得nx和ex成为了一个原子操作。命令如下
    在这里插入图片描述
分布式锁的超时问题

分布式锁并不能解决超时问题,如果在加锁和释放锁中间的代码逻辑执行时间过长,导致还没有释放锁,这个锁就过期了。那么这个时候第二个线程就可以去向redis申请这个过期的锁作为自己的锁。这里就违背了redis锁的唯一性原则。
解决办法:
1.人工干预
2.使用lua脚本去给redis服务器发送请求,申请延时锁的释放时间。

redis的简单延时队列

因为list的数据结构,所以redis可以用来做一个简单的延时队列。
客户端通过redis的延时队列pop来获取消息,然后进行处理,如果该队列空了, 那么客户端就会陷入pop的死循环,也就是空轮询,这会拉高客户端的cpu,也会拉高redis的QPS。
解决的办法:
1.可以sleep当前线程一会
2.但即使sleep的再短,也会造成一定的延时,所以可以使用阻塞读,blpop。阻塞读会在队列没有数据的时候,进入休眠状态,当数据到来时,立马醒过来。
3.如果一直阻塞,那么就会造成空闲连接问题,闲置过久,redis可能会主动断开该连接,以减少闲置资源占用,这个时候blpop会抛出异常。所以客户端要注意捕获异常。

redis的位图数据结构

假设有这么一个场景,统计用户一年的签到记录,如果用缓存实现,那么就要记录365天,如果使用普通的key value,那么就是365个key value,那么当用户增多,这个存储空间是会很大的。
为了解决上述问题,redis提供了位图数据结构。这样每一个签到(以0或1)就只占一个位,一年就是365个位,即46个字节,那么就大大节约了存储空间。
注意:二进制码的顺序是从右往左,我们数组里的顺序是从左往右
下面根据hello的ASCII码,将其存入位图结构

#以h为例
#h的ASCII码转成二进制后,就是01101000
#我们只需要设置1的值
setbit key 位置 1
#上面的位置指的就是1的位置,对于h来说,那么就是1、2、4位(这里得注意,二进制码的顺序是从右往左,我们数组里的顺序是从左往右)。
#所以对于h的设置
setbit key 1 1
setbit key 2 1
setbit key 4 1
get key   打印会发现是h,如下图所示

在这里插入图片描述
再继续对key插入e的bit,那么就如下图一样
在这里插入图片描述

上面的操作可以理解为“零存整取”,即存的是字节,取的是字符。
那么当然也可以零存零取,整存零取

  • 对于整存零取,我们set a b
    在这里插入图片描述
    可以发现,获取8个位之后,组合之后是01100010,对应的值是不是97,不正是a吗?
  • 对于零存零取,只设置了b的第一位是1,所以取第二位是返回的是0 在这里插入图片描述
bitcount和bitpos
  • bitcount用来查找指定字符范围内出现的1的次数
    bitcount key start end
    在这里插入图片描述
  • bitpos用来查找指定字符范围内的第一个位的的位置
    在这里插入图片描述

HyperLogLog(高级数据结构)

Redis 提供了 HyperLogLog 数据结构就是用来解决这种统计问题的。HyperLogLog 提供不精确的去重计数方案,虽然不精确但是也不是非常不精确,标准误差是 0.81%,HyperLogLog 数据结构是 Redis 的高级数据结构。
其常用操作有pfadd,pfcount,pfmerge。pfmerge用于合并多个pfadd的值。
HyperLogLog可以用于大量用户相关的数据。对比set存储,可以说是非常高效的。其只需要占据12k的存储空间。这个可以解决许多精确不高的统计需求。
pfadd的用法类似于set集合的sadd,放入数据,计数+1
pfcount获取当前计数。

在这里插入图片描述
使用pfmerger,将指定的key当前的里面的元素数量追加到前面的指定的key的元素数量上。这个时候fruit的计数就变成了5,但其内部元素还是3
在这里插入图片描述

布隆过滤器

HyperLogLog这样的数据结构可以用来估计大概的值。但如果我们想知道某一个值是不是存在于其中,HyperLogLog并没办法做到这一点。

讲一个使用场景,我们在使用某app看推荐内容的时候,系统会自动推荐新的内容,它需要去重我们看过的内容,那么它是怎么实现的去重?
1、如果是遍历整个服务器的历史记录,将其存入数据库,那么对于数据库的exists查询请求就很频繁了,当并发量高的时候,数据库可能就会扛不住。
2、如果将整个缓存起来,那么浪费的存储空间太大了。时间一长,不仅存不下,性能也会下降。

这个时候,就可以采用布隆过滤器,可以专门解决这类问题,在达到去重的同时,在空间上还能节省90%以上。

布隆过滤器是什么

可以将其理解成一个不怎么精确的set结构。当我们使用它的contains方法判断某个对象是否存在时,它可能会误判。但可以人为将这个误判的几率调到很小。

布隆过滤器对于去重是这样的。它认为没见过的就一定是没见过的,它认为见过的(去重),那么可能是它没见过的(因为一些长得比较像的系数组合,导致它误以为它见过,这也是精度误差所在)。

Redis提供的布隆过滤器在Redis4.0提供插件功能之后才正式登场。

可以通过调整bf.reserve的参数来更改误判率。bf.reserve有三个参数,分别是key,error_rate(默认0.01),initial_size(默认100)。

布隆过滤器的error_rate(错误率)越小,需要的initial_size的值就越大。但initial_size的值越大,会浪费存储空间,估计的过小,影响准确率。所以在使用布隆过滤器前,要尽可能的精确估计好元素数量,还需要加上一定的冗余空间,避免实际元素数量大于估计元素数量。

布隆过滤器的原理

内部其实是一个位数组和多个散列性极好的hash函数。当要添加一个key时,就会经过多个hash散列函数的计算,得出具体的存放位置, 将具体的存放位置改为1。当需要查询该key时,也要通过多个hash函数去计算,对比每一个位置上是不是都是1,如果有一个不是,那么就判断该key不存在。

这个位数组越长,错误率越低,需要的hash函数的最佳数量也会增多,影响计算的效率。
可以通过网页上的布隆计算器,计算出具体的数值

布隆过滤器一些的常用场景
  • 可以用在爬虫去重网页URL上。
  • redis的缓存穿透,就是可以通过布隆过滤器来过滤掉大多数无用的请求。来降低数据库压力的。

这篇关于Redis学习与入门----深度历险(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/404920

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

数论入门整理(updating)

一、gcd lcm 基础中的基础,一般用来处理计算第一步什么的,分数化简之类。 LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; } <pre name="code" class="cpp">LL lcm(LL a, LL b){LL c = gcd(a, b);return a / c * b;} 例题:

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多