热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Redis数据结构之string应用场景解析

本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。


英国弗兰明曾说过一句话:“不要等待运气降临,应该去努力掌握知识。”


1 前言


大家好,我是阿沐!对于redis大家是最熟悉不过了,作为缓存界的使用率一直遥遥领先。基本上整个互联网无论大小公司使用redis占绝大部分,那么很多人使用它,那就是只是使用它,对于它的使用场景并没有去理会太多(能用就行),这篇文章来讲讲redis的基础数据结构string。


Redis有五种基础数据结构如下: string(字符串)hash(哈希又叫字典)set(集合又可叫无序集合)zset(有序集合)list(列表) 。这五种数据结构涵盖了整个redis的知识点,它是redis最重要却也是最 基础的知识点 。当然,你也会在面试中经常遇到redis这些基础知识,三连问:它的使用场景?它的坑在哪里?如何解决?那么下面阿沐将会使用面试官与面试者的角色一一的进行问答讲解!


2 面试开场心里状态


面试官:一个带着眼镜的年轻面试官(26,27岁)缓慢从原来走过来,手里拿着我的简历,边走边看,然后时不时抬头望向我这边。


面试者:“哎呦,,,别再看我了,我就是一个一年经验的小菜鸟,看的我心发慌,腿直抖。他会问我啥呢,我该怎么流畅的回答呢?心里嘀嘀咕咕, 真的我还是只是一个孩子啊 ,面试官放过我吧,简单点来。”


面试官:“来到面试会议室坐在我对面”。简单的介绍下自己吧?


面试者:你好面试官,我叫阿沐,河南人,毕业于郑州大学。目前工作半年,上家公司是广州xxx公司,担任后端开发工程师,主要负责搜素,购物车,产品模块等。业余时间喜欢xxxx(省略200字)......


面试官:嗯呢,看了你的简历,那么我们简单聊聊redis,看你上面写的项目大部分都是跟redis相关连。就说一说redis的string类型的一些常用的基础指令吧!


3 面试开始直进主题了


这不是小菜一碟嘛,难不住我,easy:



  1. 相关描述:string是数据结构中比较简单的一种,通常是以key和value的形式存储,它内部存储是一个字符数组形式。Redis字符串的最大长度是512M,并且的存储是动态的(意味着可以随时修改它本身的值),每次分配内存时会高出实际字符串的length,这样采用 预分配冗余空间方式来减少内存的频繁分配


  2. 常使用命令:



set - 设置key的值;若key存在,覆盖旧值无视类型,成功返回OK(注意大写OK,很多框架封装返回的值改变了,不要被误导了)
get - 获取指key值;若key不存在,返回nil(不是null,不要搞错)
mset - 同set一致,批量设置键值对,减少网络开销
mget - 同get一致,批量获取键值对,减少网络开销
incr - key值+1,不存在则先set后incr,返回integer切记值必须能标识为数字
incrby - 同incr一致,多出一个指定数字增量值
decr - 同incr原理一致,操作方式变为了减法
decrby - 同incrby原理一致,操作方式变为了减法
strlen - 获取长度len,若不存在返回0,类型为integer
setnx - 设置key值,若key不存在,则返回1,否则返回0。(若场景需要设置过期时间,不推荐使用这个命令,网络波动情况下,有可能setnx成功,expire却失败了,不是原子操作)
setex - 设置key值及过期时间,若key已存在,则替换旧值覆盖。(若需要set值且需设置过期时间且要求较高必须要有过期时间,推荐使用这个命令,设置key值+过期时间是原子操作,要么成功要么失败)
append - 对key值进行末尾追加数据,返回值是字符串长度

面试官:微微点点头,心里说道:“哎呦,功课做得挺足的哦,对使用和注意点了解的这么清楚,来自面试官的肯定,加分”。


4 string的使用场景


面试者:就知道你会问我使用场景,还好我准备的比较充足,是时候表现真正的技术啦!面带表情开始一本经常的介绍到: 计数缓存基础数据限制请求次数分布式下共享session签到 等等,那么我按照这些场景介绍下用法。


4.1 缓存基础数据


例如缓存登录用户的基本的缓存数据(建议hash存储,这里只是举例),用户登录成功之后,可以将用户基础数据组装json字符串设置到缓存,用户请求查询缓存层来加速读写性能降低mysql查询的压力。若用户更新则对应的刷新同步缓存,如果用户表数据字段较多,可以分成冷热数据分别存储,降低key的大小,减少网络额外开销。


// 实例化redis
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
//echo "Server is running: " . $redis->ping();
$member_info = [
'member_id' => 1001,
'member_nickname' => '我是阿沐',
'member_email' => 'lw1772363381@163.com',
'member_phone' => '12345678998',
'member_qq' => '2511221051@qq.com',
'member_level' => 120,
];
$key = sprintf('member:info:id:%u', $member_info['member_id']);
$expire = 60;
// 设置缓存并且60s过期
$result = $redis->set($key, json_encode($member_info), $expire);
//$redis->set($key, json_encode($member_info), ['nx', 'ex' => 60]);
if (!$result) {
exit( "额,设置失败了");
}
echo "哇喔,设置成功了";
-- 终端 get
127.0.0.1:6379> get member:info:id:1001
"{\"member_id\":1001,\"member_nickname\":\"\\u6211\\u662f\\u963f\\u6c90\",\"member_email\":\"lw1772363381@163.com\",\"member_phone\":\"12345678998\",\"member_qq\":\"2511221051@qq.com\",\"member_level\":120}"
127.0.0.1:6379> get member:info:id:1001
(nil) -- 记住无数据返回nil

4.2 计数器


计数功能常用于统计某一个页面的访问数据量,例如:我之前是在电商公司工作,产品会经常让我们做活动 页面的浏览量 以及活动内的产品详情打开量,通过 pv数据分析 活动中哪些产品受众。当然也会统计产品详情页 视频的点击量 ,以及开放api的每天调用总次数等等场景。迅速在纸上手写代码:


// 实例化redis
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
//echo "Server is running: " . $redis->ping();
$key = sprintf('product:detail:pid:%u', 12345);
$result = $redis->incr($key);
// $result php中返回的是布尔类型 true or false
//redis终端
127.0.0.1:6379> incr product:detail:pid:12345
(integer) 2
127.0.0.1:6379> get product:detail:pid:12345
"2"

4.3 限制请求次数


限制访问次数,这个一般用来控制某一图谋不轨的人利用非正常手动 刷接口 或者 恶意破坏我们的系统服务 ,对于一些比较敏感,比较重要的接口做上限流措施。例如:网站使用手机号注册,一般公司短信发送是使用第三方,是要给钱的,心疼啊!点一次就是1毛钱。心在滴血。尽管客户端做了校验限制,但是避免不了抓包模拟请求,这个时候我们需求对这些用户或者IP地址进行限流请求,使用incr+expire结合处理。手撕伪代码如下:


// 实例化redis
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
//echo "Server is running: " . $redis->ping();
$key = sprintf('member:login:phone:count:%u', 12345678998);
$maxCount = 10;
// 获取当前key是否有生存时间 -2未设置key -1 key 存在未设置过期时间
$ttl = $redis->ttl($key);
if ($ttl > 0) {
$times = $redis->get($key);
if ($times >= $maxCount) return true;
$result = $redis->incr($key);
if ($result) return true;
} else {
$result = $redis->set($key, 1, 60);
if ($result) return true;
}
return false;

4.4 分布式共享session


面试者:“心里暗暗庆幸着,还好我在上家公司有实习了解过,不然真的要死翘翘了“。开始吹牛皮了:系统最开始时由于用户量小一般都是 单机器支撑 ,用户登录之后存储在 session (服务器文件存储)中,那么当随着我们的业务越来越大,用户量激增到上百万,这个时候我们有单机变成了集群,使用了 ngxin做负载均衡 ,将各个用户的请求都会被负载到各个服务器上,用来分摊机器压力,保证服务稳定。


这个时候会发现用户登录时请求随机到了某一台机器,这个时候生成了session文件,但是在用户访问其他页面又被随机到其他服务器这个时候拿不到session,就会被拦截用户登录请求,就导致用户可能要登录很多次。那这样肯定不行呀,非常不友好的用户体验会被的狗血淋头。


那么这个时候分布式缓存就可以解决这个问题啦。用Redis将用户的Session信息进行集中的管理,每次用户登录信息都从Redis中集中获取,这样完美的解决了这一问题。我们可以设置php.ini:


session.save_handler = redis -- 保存方式
session.save_path ="tcp://127.0.0.1:6379" -- 保存路径


面试官:”这家伙准备的这么充分,头头是道的,条理清晰。看来我得给他加点调料了,戳戳锐气“。面试官说道:请问负载均衡集群中的session解决方案除了使用redis管理session,还有哪些方案可以使用呢?


面试者:”心里暗香:吆喝,这是跟我杠上了,问的差不多不就行了,还非要深入去问;我这一年多经验的人,就对我这么狠,求求你饶过小弟吧!不过我初生不怕牛犊,我就是刚“。恩,既然您问到这一块了,那我也就对号入座了,说下我自己了解到的吧:


其实除了session使用redis存储这一方案,我们还可以考虑权衡一下方案:



  1. session会话保持- 意思是保证用户每次请求都在同一台机器上


  2. session会话复制- 意思是把每个应用服务器中的session信息复制同步到其它服务器节点



4.4-1 session会话保持


-1、一般使用nginx负载均衡中的upstream中的一种分配方式ip_hash或者url_hash(需要额外install) 原理就是:将用户的每一个请求ip地址hash结果集进行分配到固定一台服务器请求,这样就可以保证用户一直处于登录状态。例如下面配置:


upstream bakend { -- bakend是upstream的名称,也是proxy_pass反向代理的地址
ip_hash;
server 192.168.0.1:80;
server 192.168.0.2:80;
server 192.168.0.3:80;
}

2、使用haproxy做负载均衡的Session保持,有三种方式:



  • ① 类似nginx的ip_hash识别算法


  • ② COOKIE 识别,haproxy在用户第一次访问的后在浏览器插入一个COOKIE,用户下一次访问的时候浏览器就会带上这个COOKIE给Haproxy,进行识别分析


  • ③ haproxy将后端服务器产生的session和后端服务器标识存在haproxy中的一张表里,请求时先查询这张表进行识别(感兴趣的可以自行谷歌查阅haproxy)



会话保持的缺点:


1、负载均衡无用武之地:本身面对 大流量、高并发、我们使用nginx做负载均衡,降低服务压力 ;那么会话保持显然摒弃了这一点优势,每个用户规定请求某台机器,可能会出现多台机器资源空缺浪费,而部分机器压力负载过大,倾向于单机时代。


2、无法根据解决session问题:若某台服务器突然宕机或者超时链接不上,那么nginx的高可用则会踢出这台机器,请求会被重新分配到其他机器,导致用户要重新登录,这体验度简直不要不要的,差评。


4.4-1 session会话复制


会话复制是一种服务机制,用于复制存储在不同实例的会话中的数据。主要是指在集群环境下, 多台服务器之间同步session文件数据 ,保持所有机器上的session一致,对外透明。如果用户登录之后,请求被随机分配到任何一台机器都能通过session会话拿到登录信息,或者某台机器服务故障宕机, nginx负载均衡调度器会遍历可以使用的节点 ,分发其他机器请求。但是session已完成同步,不会影响用户再次登录。


不推荐使用原因:session的会话复制会带来额外的性能损失,一旦session中存在比较大的对象,会导致同步缓存,性能消耗上升。


最后总结


面试者:面试官你好,上面说的这些就是我在项目中经常使用的字符串类型,并且还有它的使用场景以及场景分析和出现的问题以及解决方案。


面试官:嗯嗯,可以看出来redis的基础知识还是很扎实的,常使用命令的一些分析的很好,也很仔细。看出来你在日常工作中善于积累," 来自面试官的一个肯定的眼神 "。那么我们来继续下一个问题(等待下一章节)...


从上面的面试情况来分析,基本上常使用的一些指令都涵盖到以及使用的场景介绍;并非一定是这样使用,只是我们在实际使用string类型指令要考量我们的场景,根据用户量或者其他方面来决定使用的类型,考量设置的数据量,太大影响性能消耗;那么我们下一章节继续。


宫崎骏曾说过一句话:“遇见的都是天意,拥有的都是幸运,不完美又何妨,万物皆有裂痕,那是光照进来的地方”;我们学习亦是如此,唯有面试官虐我千百遍,我才能待面试官如初恋;不完美才有提升的机会。


好了,我是阿沐,一个不想30岁就被淘汰的打工人 :fuelpump:️ :fuelpump:️ :fuelpump:️ 。




推荐阅读
  • 微信官方授权及获取OpenId的方法,服务器通过SpringBoot实现
    主要步骤:前端获取到code(wx.login),传入服务器服务器通过参数AppID和AppSecret访问官方接口,获取到OpenId ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 使用在线工具jsonschema2pojo根据json生成java对象
    本文介绍了使用在线工具jsonschema2pojo根据json生成java对象的方法。通过该工具,用户只需将json字符串复制到输入框中,即可自动将其转换成java对象。该工具还能解析列表式的json数据,并将嵌套在内层的对象也解析出来。本文以请求github的api为例,展示了使用该工具的步骤和效果。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法
    本文介绍了解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法,包括检查location配置是否正确、pass_proxy是否需要加“/”等。同时,还介绍了修改nginx的error.log日志级别为debug,以便查看详细日志信息。 ... [详细]
  • 本文介绍了一个React Native新手在尝试将数据发布到服务器时遇到的问题,以及他的React Native代码和服务器端代码。他使用fetch方法将数据发送到服务器,但无法在服务器端读取/获取发布的数据。 ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • 本文介绍了在rhel5.5操作系统下搭建网关+LAMP+postfix+dhcp的步骤和配置方法。通过配置dhcp自动分配ip、实现外网访问公司网站、内网收发邮件、内网上网以及SNAT转换等功能。详细介绍了安装dhcp和配置相关文件的步骤,并提供了相关的命令和配置示例。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
author-avatar
落了个小妞农_602
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有