Redis --- 第四讲 --- 常用数据结构 --- string类型

news/2024/10/17 19:44:30/

一、认识数据类型和编码方式

有序集合,相当于除了存储member之外,还需要存储一个score(权重,分数)

Redis底层在实现上述数据结构的时候,会在源码层面,针对上述实现进行特定的优化,来达到节省时间/节省空间的效果。特定的优化:内部具体实现的数据结构(编码方式),会有一定的变数。redis承诺,现在我这里有个hash表,你进行查询,插入,删除,操作都保证是O(1)。但是这个背后的实现,不一定就是一个标准的hash表。可能在特定场景下,使用别的数据结构实现,但是仍然保证时间复杂度符合承诺。

数据结构:redis承诺给你的,也可以理解成数据类型。

编码方式:redis内部底层的实现。

同一个数据类型,背后可能的编码实现方式是不同的,会根据特定场景进行优化。

raw:最基本的字符串。底层就是持有一个char数组,或者byte数组。

int:redis通常可以用来实现一些技术这样的功能。当value就是一个整数的时候,此时可能redis会使用int来保存。

embstr:针对短字符串进行的特殊优化。redis会自动适应,程序员在使用redis的时候没有感觉

hashtable:最基本的哈希表。redis内部的哈希表的实现。虽然和java实现方式可能不太一样,但是整体思想适合之前学过的一致的。

ziplist:压缩链表。在哈希表里面元素比较少的时候,可能优化成ziplist了。压缩列表,能够节省空间。为啥要压缩?redis上有很多的key,可能是某些key的value是hash,此时,如果key特别多,对应的hash也特别多,但是每个hash又不大的情况下,就尽量去压缩,压缩之后就可以让整体占用的内存更小了。

linkedlist:链表,元素个数多使用链表的结构实现。从redis3.2开始,引入了新的实现方式quicklist,同时兼顾了linkedlist和ziplist的优点。quicklist就是一个链表,每个元素又是一个ziplist,把空间和效率都折中兼顾到。quicklist比较类似于C++中的std::deque。

intset:集合中存的都是整数 

skiplist:链表笔试题,有一个经典的题目,复杂链表的复制。跳表也是链表,不同于普通的链表,每个节点有多个指针域,巧妙的搭配这些指针域的指向,就可以做到,从跳表上查询元素的时间复杂度为O(logN)。

使用object encoding 查看key的value的实际的编码方式。redis会自动根据当前的实际情况选择内部的编码方式,自动适应的。我们记住思想就可以了。

二、redis的单线程模型

redis只使用一个线程,处理所有的命令请求,不是说一个redis服务器进程内部真的就只有一个线程,其实也有多个线程,多个线程是在处理网络IO。假设,有多个客户端,同时操作一个redis服务器:

在上面的场景中,两个线程尝试对一个变量进行自增操作表面上看是自增两次,实际上可能只自增了一次。

当前这两个客户端,也相当于并发的发起了上述的请求,幸运的是并不会发生线程安全问题,redis服务器实际上是单线程模型,保证了当前收到的多个请求是串行执行的!多个请求同时到达redis服务器,也是要先在队列中排队,再等待redis服务器一个一个的取出里面的命令再执行。微观上讲,redis服务器是串行/顺序执行多个命令的。所以不会生成线程安全问题。

redis能够使用单线程模型,很好的工作原因主要在于redis的核心业务逻辑都是短平快的,不太消耗cpu资源也就不太吃多核了。

有很多的弊端:redis必须要特别小心,某个操作占用时间长,就会阻塞其他命令的执行!

redis虽然是单线程模型,为啥效率这么高,速度这么快呢?【重要的面试题】

参照物是数据库

1、redis访问内存,数据库访问硬盘。

2、redis核心功能,比数据库的核心功能更简单。数据库对于数据的插入删除查询,都有更复杂的支持,这样的功能势必要花费更多的开销。redis干的活少,提供的功能相比于mysql也是少了不少。

3、单线程模型:避免了一些不必要的线程竞争开销。redis每个基本操作,都是短平快的就是简单操作一下内存数据,不是什么特别消耗cpu的操作。就算搞多个线程,提升不大。

4、处理网络IO的时候,使用了epoll这样的IO多路复用机制。一个线程,就可以管理多个socket,针对TCP来说,服务器这边每次要服务一个客户端,都需要给这个客户端安排一个socket,一个服务器服务多个客户端,同时就有很多个socket,这些socket上都是无时不刻的都在传输数据吗?很多情况下,每个客户端和服务器之间的通信也没那么频繁。此时这么多socket大部分事件都是静默的。上面是没有数据需要传输的。所以,同一时刻只有少数socket是活跃的。 

以下谈到的数据结构类型都是谈value的类型的

三、Redis中的string

Redis中的字符串,直接就是按照二进制数据的方式存储的。不会做任何的编码转换,存的是啥,取出来还是啥。讲mysql的时候,知道mysql默认的字符集,是拉丁文。插入中文就会失败,Redis就没有这种机制,存的啥就是啥。一般来说,redis遇到乱码问题的概率更小。不仅仅可以存储文本数据,整数,普通的文本字符串,JSON,xml,二进制数据(图片,视频,音频……)。音频视频体积可能会比较大,Redis对于strin类型,限制了大小最大是512M,Redis是单线程模型,希望进行的操作都能比较快速。

string类型的命令学习:

set:

set key value ex 10 表示 set key value, expire key 10。执行一个命令的原子性很强。

NX 如果key不存在,才设置,如果key存在,则不设置返回nil

XX如果key存在才设置相当于更新key的value。如果key不存在,则不设置返回nil

redis文档给出的语法格式说明

[ ]相当于一个独立的单元,表示可选项(可有可无的),其中 | 表示或者的意思,多个只能出现一个。[ ]和[ ]之间是可以同时存在的

也提供了一些命令来设置NX或者XX

SETNX

SETEX

SETPX

如果key不存在,创建新的键值对。如果key存在,则是让新的value覆盖旧的value,可能会改变原来的数据类型。原来这个key的ttl(生存时间)也会失效。

一个快速失去年终奖的技巧,清楚redis上所有的数据-删库 =》mysql里的drop database。

FLUSHALL可以把reids上所有的键值对都删除。以后在公司这是一个非常危险的命令,你少个几千甚至几万块钱你应该是不愿意的。当然不排除一些富二代。但是你不难受,公司就难受了。

get:和通用命令的get相同。对于GET来说,只是支持,字符串类型的value如果value是其他类型,使用GET获取就会出错!

MSET和MGET 一次操作多组键值对

事件复杂度为O(N),此处的N不是整个redis服务器中所有key的数量,而只是当前命令中给出的key的数量。

虽然可以一次操作多组键值对,但是不能设置太多,有可能把redis给阻塞住了。

SETNX:不存在才能设置,存在则返回失败

SETEX:指定一个key的生存时间,单位second

PSETEX:和上一个一样,但是单位是毫秒

针对set的一些常见用法,进行了缩写。之所以这样设计,就是为了让操作更符合人的直觉(使用者的门槛就越低,要背的东西就越少)。符合人的直觉,编程语言中,很多的关键字,都是和自然语言相关的。后续我们去设计一些库,设计一些工具,代码给别人使用的时候,也要尽量符合直觉,不要设计反人类,反直觉。

INCR 针对value + 1  语法:INCR key。此时key对应的value必得是整数,此操作的返回值,就是+1之后的值。incr操作的key如果不存在,就会把这个key的value当做0来使用。以下命令都是这样的操作。

INCRBY 针对value + n 加负数也是可以的。

DECR 针对value -1 返回值也是计算后的值。

DECRBY 针对value - n

INCRBYFLOAT 针对value +/-小数 ,把key对应的饿value进行+ -运算,运算的操作数可以是浮点数,虽然此处没有提供减法版本的命令,但是使用redis进行的计数操作,一般都是针对整数来进行的。

上述操作的时间复杂度都是O(1)的。由于redis处理命令的时候,是单线程模型,多个客户端同时针对同一个key进行incr操作,不会引起 "线程安全" 问题。

字符串也支持一些常用的操作,拼接,获取/修改字符串的部分内容,获取字符串长度。

append 如果key已经存在并且是一个string,命令会将value追加到原有string的后边

append返回值,长度的单位是字节!redis的字符串不会对字符编码做任何处理,当前xshell终端,默认的字符变编码是utf8,在终端中输入汉字之后,也就是按照utf8编码的,一个汉字再utf8字符集中,通常是3个字节的。

在启动redis客户端的时候,加上--raw这样的选项。就可以使redis客户端能够自动的把二进制数据尝试翻译。

getrange  获取子串  是一个闭区间。C++和JAVA中,谈到一个区间,大多都是前闭后开。 正常下标都是从0开始的整数,redis的下标使可以支持负数的。-1倒数第一个元素。

setrange 切出的字串进行修改 offset偏移量,从第几个字节开始进行替换。替换多长看实际value的长度。返回值是替换之后,新的字符串的长度。如果当前咱们value是一个中文字符串,进行setrange的时候,是可能有问题的。会导致编码失败。针对不存在的key凭空生成了一个字节,这个字节里的内容就是0x00,aaa就被追加到0x00后面了。setrange针对不存在的key也是可以操作的,不过会把offset之前的内容填充成0x00。

strlen 获取到字符串的长度,单位是字节。C++中,字符串的长度本身就是用字节为单位,java中字符串的长度则是以字符为单位的,MySQL中varchar(char)此处N的单位就是字符,MySQL中的字符,也是完整的汉字,这样的一个字符,也可能是多个字节。

,当key的value不是string时会报错。

string命令总结

string的编码方式

int :8个字节的长整型

embstr :小于等于39个字节的字符串

raw:大于39个字节的字符串

通过object encoding key查看编码格式。不建议大家去记,长度大于39这样的数字。某个业务场景,有很多的key,类型都是string,但是每个value的string长度都是100左右,更关注于整体的内存空间,因此这样的字符串使用embstr来存储也不是不能考虑,上述效果具体怎么实现?

1、先看redis是否提供了对应的配置项,可以修改39这个数字。

2、如果没有提供配置型,就需要针对redis源码进行魔改。

reids存储小数,本质上还是当作字符串来存储,这就和整数相比差别很大了,整数直接使用int来存。小数则是使用字符串来存,意味着每次进行算数运算,都需要把字符串转换成小数,进行运算,结果再转会字符串进行保存。

string的应用场景

缓存功能

整体的思路:应用服务器访问数据的时候,先查询Redis,如果Redis上数据存在了,就直接从Redis取数据交给应用服务器,不继续访问数据库了。如果Redis上数据不存在,再读取MySQL把读到的结果,返回给应用服务器,同时,把这个数据也写入到Redis中。Redis这样的缓存,经常用来存储热点数据,高频被使用的数据,这个定义方式,结合业务场景时有很多种方式。刚才上述描述的过程,相当于是把最近使用到的数据作为热点数据。暗含了一层假设,某个数据一旦被用到了,那么很可能在最近这段时间就会被反复用到。

上述策略存在一个明显的问题:

随着时间的推移,肯定会有越来越多的key在redis上访问不到,从而从mysql中读取写入redis了。此时redis中的数据会越来越多。针对这种情况我们会有一定的解决方法。

1)把数据写给redis的同时,给这个key设置一个过期时间。

2)Redis也在内存不足的时候,提供了淘汰策略。详细内容后面再说。

所以我们不用太担心上述的问题。

计数(Counter)功能

企业为啥乐意收集用户的数据?

统计 =》 进一步明确用户的需求=》根据需求改进和迭代产品。Redis并不擅长数据统计。比如想在上述的Redis中,统计,播放量前100的视频有哪个 --- 基于Redis搞就很麻烦。相比之下,如果是mysql来存储上述数据,一个sql就搞定了。

异步方式:这里写入统计数据仓库的步骤。异步的,不是说,来一个播放请求,这里就必须立即马上写一个数据。

 实际中要开发一个成熟、稳定的真实计数系统,要面临的挑战远不止如此简单:防作弊,按照不同维度计数,避免单点问题,数据持久化到底层数据源等。

共享会话

Session会话

Cookie浏览器存储数据的机制,Session服务器这边存储数据的机制,都是键值对的形式出现的。

如果每个应用服务器,维护自己的会话数据,此时彼此之间不共享。用户请求访问到不同的服务器上,就可能会出现一些不能正确处理的情况。

此时所有的会话数据,都被各个服务器共享了。

短信验证码:

手机验证码:

1、生成验证码,用户输入以下手机号,点击获取验证码,限制1分钟之内,最多获取5次验证码。用户每次获取验证码必须间隔30秒

2、检查验证码。把短信收到的验证码这一串数,提交到系统中,系统进行验证,验证码是否正确。

像发送短信这样的操作,都是由专门的SDN来实现的(第三方平台来提供服务的),但是需要充值使用。

业务是什么?

业务其实就是一个公司/一个产品,是如何解决一个/一系列问题的。解决问题的过程就成为是业务。一个公司/产品要想生存,就得赚钱,要想赚钱,就得能帮别人解决问题。


http://www.ppmy.cn/news/1539789.html

相关文章

mybatis-plus的Iservice接口的save方法,返回true,但是数据库表里却没有看到新记录

背景 需求是记录下操作过数据库的行为,将其记录到一个操作行为表中,我选择了使用aop特性来实现这一功能,发现当操作行为一切正常时,这个mybatis的save方法正常执行,行为表里正常增加记录。但是当操作行为抛出异常时&a…

Pandas缺失值处理

目录 NaN 加载包含缺失的数据 查看缺失值 通过info函数查看缺失值 通过isnull函数查看缺失值 通过notnull函数查看缺失值 通过isnull().sum()统计空值 缺失值处理 准备数据 dropna删除缺失值 fillna平均值填充缺失值 fillna前后值填充缺失值 interpolate线性插值 …

《京东金融APP的鸿蒙之旅系列专题》鸿蒙工程化:Hvigor构建技术

作者:京东科技 杨拓 一、构建工具概述 Hvigor构建工具是一款基于TypeScript实现的构建任务编排工具,专为提升构建和测试应用的效率而设计。它主要提供以下关键功能: 1.任务管理机制:包括任务注册和编排,帮助开发者高效…

2024 kali系统2024版本,可视化界面汉化教程(需要命令行更改),英文版切换为中文版,基于Debian创建的kali虚拟机

我的界面如下所示 1. 安装 locales sudo apt install locales 2. 生成中文语言环境 sudo locale-gen zh_CN.UTF-8 如果你希望安装繁体中文,可以加入: sudo locale-gen zh_TW.UTF-8 3. 修改 /etc/default/locale 文件 确保有以下内容 LANGzh_CN.UT…

文本生成视频技术:艺术与科学的交汇点

在人工智能技术的飞速发展下,文本生成视频(Text-to-Video)技术已经成为现实。这项技术能够根据文本描述生成相应的视频内容,极大地拓展了内容创作的边界。本文将从三个主要方面对文本生成视频技术进行深入探讨:技术能达…

解锁C++多态的魔力:灵活与高效的编码艺术(上)

文章目录 前言🌸一、多态的定义与概念🌻1.1 多态的核心思想:🌻1.2 多态的两种主要形式: 🌸二、多态的使用条件🌻2.1 基类指针或引用2.1.1 为什么需要基类指针或引用 🌻2.2 虚函数&am…

【计算机网络】详解IP协议网段划分路由转发子网掩码网络号

一、IP功能 IP可以实现主机定位和路由选择,提供一种能力,将数据可靠地从A点跨网络送到B点。数据先根据目的IP在局域网之间进行转发,再在局域网内进行内网转发。 二、IP协议报头 4 位版本号(version):指定 IP 协议的版本&#xff…

SpringAI快速上手

一、导入依赖 镜像&#xff08;导入maven依赖&#xff09; <repositories><repository><id>spring-snapshots</id><name>Spring Snapshots</name><url>https://repo.spring.io/snapshot</url><releases><enabled>…