Subject:主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如爬虫、机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者。
SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;可以看出它是shiro的核心, SecurityManager相当于spring mvc中的dispatcherServlet前端控制器。
Realm:域,shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。
Shiro运行流程(比如登录流程) 1、首先调用Subject.login(token)进行登录,他会委托给SecurityManager
2、SecurityManager负责真正的身份验证逻辑;它会委托给Authenticator进行身份验证;
3、Authenticator会把相应的token传入Realm,从Realm获取身份验证信息,如果没有就返回认证失败,有的话就继续执行操作。
1、在 web.xml 中配置 Shiro 的 Filter
2、在 Spring 的配置文件中配置 Shiro
3、配置自定义 Realm:实现自定义认证和授权
4、配置 Shiro 实体类使用的缓存策略
5、配置 SecurityManager
6、配置保证 Shiro 内部 Bean 声明周期都得到执行的 Lifecycle Bean 后置处理器
7、配置AOP 式方法级权限检查
8、配置 Shiro Filter
RESTful与技术无关,代表的是一种软件架构风格,就是日常我们前后端开发遵循的一套接口规范.
RESTful处理资源的动作有哪些?1.swagger2
2 小么鸡
3.Apizza
4.Yapi
列举工作中常用的几个git命令? 新增文件的命令:git add file或者git add .
提交文件的命令:git commit –m或者git commit –a
查看工作区状况:git status –s
拉取合并远程分支的操作:git fetch/git merge或者git pull
查看提交记录命令:git reflog
开发过程中,我们都有自己的特性分支,所以冲突发生的并不多,但也碰到过。诸如公共类的公共方法,我和别人同时修改同一个文件,他提交后我再提交就会报冲突的错误。
发生冲突,在IDE里面一般都是对比本地文件和远程分支的文件,然后把远程分支上文件的内容手工修改到本地文件,然后再提交冲突的文件使其保证与远程分支的文件一致,这样才会消除冲突,然后再提交自己修改的部分。特别要注意下,修改本地冲突文件使其与远程仓库的文件保持一致后,需要提交后才能消除冲突,否则无法继续提交。必要时可与同事交流,消除冲突。
发生冲突,也可以使用命令。
通过git stash命令,把工作区的修改提交到栈区,目的是保存工作区的修改;
通过git pull命令,拉取远程分支上的代码并合并到本地分支,目的是消除冲突;
通过git stash pop命令,把保存在栈区的修改部分合并到最新的工作空间中;
如果想撤销提交到索引区的文件,可以通过git reset HEAD file;如果想撤销提交到本地仓库的文件,可以通过git reset –soft HEAD^n恢复当前分支的版本库至上一次提交的状态,索引区和工作空间不变更;可以通过git reset –mixed HEAD^n恢复当前分支的版本库和索引区至上一次提交的状态,工作区不变更;可以通过git reset –hard HEAD^n恢复当前分支的版本库、索引区和工作空间至上一次提交的状态。
你使用过git stash命令吗?你一般什么情况下会使用它? 命令git stash是把工作区修改的内容存储在栈区。
以下几种情况会使用到它:
解决冲突文件时,会先执行git stash,然后解决冲突;
遇到紧急开发任务但目前任务不能提交时,会先执行git stash,然后进行紧急任务的开发,然后通过git stash pop取出栈区的内容继续开发;
切换分支时,当前工作空间内容不能提交时,会先执行git stash再进行分支切换;
查看分支的提交历史记录:
命令git log –number:表示查看当前分支前number个详细的提交历史记录;
命令git log –number –pretty=oneline:在上个命令的基础上进行简化,只显示sha-1码和提交信息;
命令git reflog –number: 表示查看所有分支前number个简化的提交历史记录;
命令git reflog –number –pretty=oneline:显示简化的信息历史信息;
如果要查看某文件的提交历史记录,直接在上面命令后面加上文件名即可。
注意:如果没有number则显示全部提交次数。
简单来说:git fetch branch是把名为branch的远程分支拉取到本地;而git pull branch是在fetch的基础上,把branch分支与当前分支进行merge;因此pull = fetch + merge。
使用过git merge和git rebase吗?它们之间有什么区别? 简单的说,git merge和git rebase都是合并分支的命令。
git merge branch会把branch分支的差异内容pull到本地,然后与本地分支的内容一并形成一个committer对象提交到主分支上,合并后的分支与主分支一致;
git rebase branch会把branch分支优先合并到主分支,然后把本地分支的commit放到主分支后面,合并后的分支就好像从合并后主分支又拉了一个分支一样,本地分支本身不会保留提交历史。
GitFlow可以用来管理分支。GitFlow工作流中常用的分支有下面几类:
master分支:最为稳定功能比较完整的随时可发布的代码,即代码开发完成,经过测试,没有明显的bug,才能合并到 master 中。请注意永远不要在 master 分支上直接开发和提交代码,以确保 master 上的代码一直可用;develop分支;用作平时开发的主分支,并一直存在,永远是功能最新最全的分支,包含所有要发布 到下一个 release 的代码,主要用于合并其他分支,比如 feature 分支; 如果修改代码,新建 feature 分支修改完再合并到 develop 分支。所有的 feature、release 分支都是从 develop 分支上拉的。feature分支;这个分支主要是用来开发新的功能,一旦开发完成,通过测试没问题(这个测试,测试新功能没问题),我们合并回develop 分支进入下一个 releaserelease分支;用于发布准备的专门分支。当开发进行到一定程度,或者说快到了既定的发布日,可以发布时,建立一个 release 分支并指定版本号(可以在 finish 的时候添加)。开发人员可以对 release 分支上的代码进行集中测试和修改bug。(这个测试,测试新功能与已有的功能是否有冲突,兼容性)全部完成经过测试没有问题后,将 release 分支上的代码合并到 master 分支和 develop 分支hotfix分支;用于修复线上代码的bug。**从 master 分支上拉。**完成 hotfix 后,打上 tag 我们合并回 master 和 develop 分支。
GitFlow主要工作流程1.初始化项目为gitflow , 默认创建master分支 , 然后从master拉取第一个develop分支2.从develop拉取feature分支进行编码开发(多个开发人员拉取多个feature同时进行并行开发 , 互不影响)3.feature分支完成后 , 合并到develop(不推送 , feature功能完成还未提测 , 推送后会影响其他功能分支的开发);合并feature到develop , 可以选择删除当前feature , 也可以不删除。但当前feature就不可更改了,必须从release分支继续编码修改
4.从develop拉取release分支进行提测 , 提测过程中在release分支上修改BUG
5.release分支上线后 , 合并release分支到develop/master并推送;合并之后,可选删除当前release分支,若不删除,则当前release不可修改。线上有问题也必须从master拉取hotfix分支进行修改;
6.上线之后若发现线上BUG , 从master拉取hotfix进行BUG修改;
7.hotfix通过测试上线后,合并hotfix分支到develop/master并推送;合并之后,可选删除当前hotfix ,若不删除,则当前hotfix不可修改,若补丁未修复,需要从master拉取新的hotfix继续修改;
8.当进行一个feature时 , 若develop分支有变动 , 如其他开发人员完成功能并上线 , 则需要将完成的功能合并到自己分支上,即合并develop到当前feature分支;
9.当进行一个release分支时 , 若develop分支有变动 , 如其他开发人员完成功能并上线 , 则需要将完成的功能合并到自己分支上,即合并develop到当前release分支 (!!! 因为当前release分支通过测试后会发布到线上 , 如果不合并最新的develop分支 , 就会发生丢代码的情况);
GitFlow的好处
为不同的分支分配一个明确的角色,并定义分支之间如何交互以及什么时间交互;可以帮助大型项目理清分支之间的关系,简化分支的复杂度。
@SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。
@ComponentScan:Spring组件扫描。
1)properties文件;
2)YAML文件;
3)系统环境变量;
4)命令行参数;
说说SpringBoot自动配置原理 SpringBoot自动配置主要是通过@EnableAutoConfiguation,@Conditional,@EnableConfigProperties,@ConfigurationProperties等注解实现的。
具体流程如下:当我们写好一个启动类后,我们会在启动类上加一个@SpringBootApplication,我们可以点开这个注解可以看到它内部有一个@EnableAutoConfiguation的注解,我们继续进入这个注解可以看到一个@import的注解,这个注解引入了一个AutoConfigurationimportSelector的类。我们继续打开这个类可以看到它有一个selectorimport的方法,这个方法又调用了一个getCandidateConfigurations方法,这个方法内部通过SpringFactoriesLoader.loadFactoryNames最终调用loadSpringFactories加载到一个meta-INF下的spring.factories文件。打开这个文件可以看到是一组一组的key=value的形式,其中一个key是EnableAutoConfiguration类的全类名,而它的value是一个xxxxAutoConfiguration的类名的列表,这些类名以逗号分隔。当我们通过springApplication.run启动的时候内部就会执行selectimports方法从而找到配置类对应的class。然后将所有自动配置类加载到Spring容器中,进而实现自动配置。
独立运行,简化配置,自动配置,应用监控,无代码生成和XML配置
yml配置相比properties配置有什么优势 (1)配置有序,在一些特殊的场景下,配置有序很关键
(2)支持数组,数组中的元素可以是基本数据类型也可以是对象
(3)简洁
直接在业务类上贴上 @Transactional 注解
MongoDB的优势有哪些?面向集合(Collection)和文档(document)的存储,以JSON格式的文档保存数据。高性能,支持document中嵌入document减少了数据库系统上的I/O操作以及具有完整的索引支持,支持快速查询高效的传统存储方式:支持二进制数据及大型对象高可用性,数据复制集,MongoDB 数据库支持服务器之间的数据复制来提供自动故障转移(automatic failover)高可扩展性,分片(sharding)将数据分布在多个数据中心,MongoDB支持基于分片键创建数据区域.丰富的查询功能, 聚合管道(Aggregation Pipeline)、全文搜索(Text Search)以及地理空间查询(Geospatial Queries)支持多个存储引擎,WiredTiger存储引、In-Memory存储引擎 问mongodb选择原因
架构简单没有复杂的连接深度查询能力,MongoDB支持动态查询。容易调试容易扩展不需要转化/映射应用对象到数据库对象使用内部内存作为存储工作区,以便更快的存取数据。 在哪些场景使用MongoDB?
规则: 如果业务中存在大量复杂的事务逻辑操作,则不要用MongoDB数据库;在处理非结构化 / 半结构化的大数据使用MongoDB,操作的数据类型为动态时也使用MongoDB,比如:
内容管理系统,切面数据、日志记录移动端Apps:O2O送快递骑手、快递商家的信息(包含位置信息)数据管理,监控数据
MongoDB 的应用已经渗透到各个领域,比如游戏、物流、电商、内容管理、社交、物联网、视频直播等,以下是几个实际的应用案例。
游戏场景,使用 MongoDB 存储游戏用户信息,用户的装备、积分等直接以内嵌文档的形式存储,方便查询、更新物流场景,使用 MongoDB 存储订单信息,订单状态在运送过程中会不断更新,以 MongoDB 内嵌数组的形式来存储,一次查询就能将订单所有的变更读取出来。社交场景,使用 MongoDB 存储存储用户信息,以及用户发表的朋友圈信息,通过地理位置索引实现附近的人、地点等功能物联网场景,使用 MongoDB 存储所有接入的智能设备信息,以及设备汇报的日志信息,并对这些信息进行多维度的分析视频直播,使用 MongoDB 存储用户信息、礼物信息等…… MongoDB在出现负载过高的情况下如何处理?
原来遇到过一次mongodb负载过高的情况,主库和从库的负载突然就上来了,CPU占有率都到了100%,这种情况下,如何处理?mongodb是做的副本集,但是主库和从库这个时候是负载同时来的。
简单点看db.currentop,看mongotop和mongostat,currentop相当于当前所有在执行的任务,看一下是在执行什么,有多少数量。也可以去slowlog里面看是否有记录,然后mongotop和mongostat是用来查看和平时比有什么异常信息。
可能的情况有连接数突然变高,查询突然变多,有一种查询没有索引,建立大表的索引等等。
Mongodb查询缓慢问题处理(看明白这个案例,然后描述自己是怎么排查问题的)https://www.cnblogs.com/seasonxin/p/8192020.html
早上突然收到配置中心服务告警,Http check出现问题,赶紧访问下服务看看是否Cabot监控系统的误报,发现服务时而正常,时而出现500内部服务器错误,赶紧排查问题。
1、问题定位
首先看看当前配置中心的总体上报statsd请求量是否出现飙升:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kr9jLsN1-1644290328223)(图片/1590395774691.png)]
确实负载上来了,先看看应用服务器有没有报错,再确定是应用服务问题,还是数据库的问题:
tail -f logs/log.log
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HNRAI2w7-1644290328224)(图片/1590395803590.png)]
应用服务器出现了一堆”no reachable servers”的报错,那么说明要么是数据库挂掉了,要么是出于各种原因无响应。
那就登录到几台数据库服务器上面,看看Mongodb进程,发现进程都还在的,那么再看看master上面的数据库日志:
ps -ef | grep mongo
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u0TDDpZx-1644290328225)(图片/1590395836421.png)]
发现日志里面出现了一堆的too many open connections: 819这样的错误。
Mongodb默认的最大连接数是819,当连接数到达819后,就无法增加,那么新的请求就无法再连接上去了。
OK,感觉问题很清晰了,时而正常时而出现500错误的原因,是因为请求量上来以后,旧的数据库连接还没有释放,新的又创建了,导致触碰到Mongodb的最大连接数限制。
2、问题处理
那么做两个方面的调整:
1)Mongodb数据库的最大连接数改大到20000(Mongodb允许的最大的连接数是20000),避免再次出现最大连接数限制:
vim mongodb.conf添加:maxConns=20000
2)在应用里面设置poolLimit,让mgo尽量多地复用现存的数据库连接,不要再无限制地增加Mongodb连接了,先设置为单个服务器最大200个连接。
Mgo里面,由于我的Mongodb集群是复制集,无法直接在mongodb的数据库连接uri里面像这样添加:
mongodb://xx:xx@10.xx.xx.xx:27017/index-config?maxPoolSize=10
因为我的复制集连接是这样的:
mongodb://xx:xx@172.19.8.76:27017,172.19.8.77:27017,172.19.9.76:27017,172.19.9.77:27017/index-config?replicaSet=configrs
查下Mgo的官方文档,看看怎么设置最大连接池数量限制,找到一项SetPoolLimit:
http://godoc.org/gopkg.in/mgo.v2#Session.SetPoolLimit
SetPoolLimit sets the maximum number of sockets in use in a single server before this session will block waiting for a socket to be available、The default limit is 4096.
这里面写道,默认情况下,Mgo的最大连接数限制是4096,那么当然高过Mongodb默认的最大819限制了,添加限制,先设置为200:
Session.SetPoolLimit(200)
都改好,重启服务,感觉又要解决一个问题了~
3、出现新的问题
打包、重新部署服务,重新刷新服务,恢复正常。
然而,没过1分钟,又收到告警邮件,看下服务,失败量比之前还多:
纳尼?!难道处理问题的姿势不对?赶紧看看Mongodb 的master服务日志:
tail -f logs/mongodb.log
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AESh4UyZ-1644290328226)(图片/1590395894876.png)]
4、继续排查问题
看到日志里面频繁出现大量的Debug 跟Info级别的COMMAND,那么估计是我的日志级别开的不对,先调整下日志级别,关闭Debug级别的各项输出:
/usr/local/mongodb/bin/mongo mongodb://xxx:xxx@172.19.8.76:27017,172.19.8.77:27017,172.19.9.76:27017,172.19.9.77:27017/admin?replicaSet=configrs
db.setLogLevel(0)
Mongodb里面,LogLevel分为0-5,其中0表示仅开启Info级别日志,1-5表示Debug级别日志的打印数量,数字越大,打印的越多。
继续查问题。先看看服务器状态:
top下看看CPU、负载、内存使用率,这不看不知道,一看吓了一大跳,CPU使用率居然高达96%,基本都被Mongodb占用了,而服务器负载高达几百,内存使用率却还剩下许多(紧急地处理问题,没有截图,宝贵的Top结果已经找不到了),说明当前的服务瓶颈出现在CPU。
首先分析是不是频繁的IO导致服务器CPU消耗过大:
iostat
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ASjOQp2t-1644290328226)(图片/1590395924099.png)]
看到IO并不是CPU高占比的原因。
重新分析一遍问题,先前是时而正常时而有问题,现在的问题比之前更频繁得多,那么跟第一次修复的连接数肯定有一定关系。
用mongostat看看服务:
/usr/local/mongodb/bin/mongostat --username=xxx --password=xxx --authenticationDatabase=admin
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ha5fVcsi-1644290328228)(图片/1590395942424.png)]
访问情况基本是正常的。
进入Mongodb看看数据库连接数:
db.serverStatus().connections
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-myYUOFvC-1644290328229)(图片/1590395959331.png)]
连接数也正常。
再看看网络状态,Establish状态的是否很多:
netstat -anp | grep ESTABLISHED
看到应用服务器确实是有正在处理的连接请求,那么结合上下文,判断应该是由于限制了最大单台机器200个连接,而申请的速度比释放的速度快,导致了大量请求阻塞在连接释放的地方,导致超时报错。
开启数据库慢查询看看:
db.setProfilingLevel(1,200)db.system.profile.find()
打印出大量的慢查询请求,集中在直接查询用户信息的user_config集合上面。
配置中心的请求分为两种,一种是全局统一数据,通过用户的不同APP版本,IMEI等信息范围相应数据的灰度版本控制,或者APP配置信息等数据,一种是用户相关,每个用户各自读写信息的数据。
前一种数据使用的是缓存机制,用户的请求命中的基本都是缓存;第二种数据太多,无法采用缓存,因此直接读写DB,当前来说,也即是user_config集合。
大量慢查询请求出现在user_config上,说明查询过慢,但IO明显不是瓶颈。
这时候我突然想到原本计划上线时候需要做,但还没做的索引,赶紧补充上去:
db.user_config.ensureIndex({"portal_id":1})
因为每次用户查询的索引值都是portal_id,所以仅需要对portal_id做索引。
为防止自己忘记,在代码里面也添加自动索引的内容:
func init() { db := mongodb.ConnectMongo() defer db.Session.Close() index := mgo.Index{ Key: []string{"portal_id"}, } db.C(CollectionUserConfig).EnsureIndex(index)}
Mongodb里面,如果不添加索引,对Mongodb数据表进行查询操作的时候,需要把数据都加载到内存。当数据的数量达到几十万乃至上百万的时候,这样的加载过程会对系统造成较大的冲击,并影响到其他请求的处理过程。
用户表当前的记录数量已经达到45万,每次都加载这么多数据,查询起来肯定是非常慢的。建立索引以后对索引字段进行查询时,仅会加载索引数据,能极大地提高查询速度。
添加索引以后,很快收到了服务恢复的邮件:
配置中心 RECOVERY.
再访问服务,正常了!
Redis 常见数据结构String,Hash,List,Set,Sorted Set
Redis 内存淘汰机制Redis中通过maxmemory参数来设定内存的使用上限,当Redis使用内存达到设定的最大值的时候,会根据配置文件中的策略选取要删除的key来删除,从而给新的键值留出空间;
redis 提供 6种数据淘汰策略:(默认策略no-eviction)
volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的)allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。4.0版本后增加以下两种:
volatile-lfu:从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰allkeys-lfu:当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的key Redis 持久化机制(怎么保证 redis 挂掉之后再重启数据可以进行恢复)Redis的一种持久化方式叫快照(snapshotting,RDB),另一种方式是只追加文件(append-only file,AOF)
快照(snapshotting)持久化(RDB):
Redis可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本。Redis创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本(Redis主从结构,主要用来提高Redis性能),还可以将快照留在原地以便重启服务器的时候使用。
AOF(append-only file)持久化
与快照持久化相比,AOF持久化 的实时性更好,因此已成为主流的持久化方案。默认情况下Redis没有开启AOF(append only file)方式的持久化,可以通过appendonly参数开启:
appendonly yes
开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是appendonly.aof。
在Redis的配置文件中存在三种不同的 AOF 持久化方式,它们分别是:
appendfsync always #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度appendfsync everysec #每秒钟同步一次,显示地将多个写命令同步到硬盘appendfsync no #让操作系统决定何时进行同步
为了兼顾数据和写入性能,用户可以考虑 appendfsync everysec选项 ,让Redis每秒同步一次AOF文件,Redis性能几乎没受到任何影响。而且这样即使出现系统崩溃,用户最多只会丢失一秒之内产生的数据。当硬盘忙于执行写入操作的时候,Redis还会优雅的放慢自己的速度以便适应硬盘的最大写入速度。
Redis 4.0 对于持久化机制的优化
Redis 4.0 开始支持 RDB 和 AOF 的混合持久化(默认关闭,可以通过配置项 aof-use-rdb-preamble 开启)。
如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的, AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差。
Redis缓存雪崩什么是缓存雪崩?
简介:缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。
有哪些解决办法?
1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个 key 只允许一个线程查询数据和写缓存,其他线程等待。
2:不同的 key,设置不同的过期时间,让缓存失效的时间点尽量均匀
什么是缓存穿透?缓存穿透说简单点就是大量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本没有经过缓存这一层。举个例子:某个黑客故意制造我们缓存中不存在的 key 发起大量请求,导致大量请求落到数据库。
1)缓存无效 key
如果缓存和数据库都查不到某个 key 的数据就写一个到 redis 中去并设置过期时间,具体命令如下:SET key value EX 10086。这种方式可以解决请求的 key 变化不频繁的情况,如果黑客恶意攻击,每次构建不同的请求key,会导致 redis 中缓存大量无效的 key 。很明显,这种方案并不能从根本上解决此问题。如果非要用这种方式来解决穿透问题的话,尽量将无效的 key 的过期时间设置短一点比如 1 分钟。
2)布隆过滤器
*布隆过滤器是一个非常神奇的数据结构,通过它我们可以非常方便地判断一个给定数据是否存在与海量数据中。我们需要的就是判断 key 是否合法,有没有感觉布隆过滤器就是我们想要找的那个“人”。具体是这样做的:把所有可能存在的请求的值都存放在布隆过滤器中,当用户请求过来,我会先判断用户发来的请求的值是否存在于布隆过滤器中。不存在的话,直接返回请求参数错误信息给客户端,存在的话才会走下面的流程
缓存击穿缓存击穿,就是说某个 key 非常热点,访问非常频繁,处于集中式高并发访问的情况,当这个 key 在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库,就像是在一道屏障上凿开了一个洞。
不同场景下的解决方式可如下:
若缓存的数据是基本不会发生更新的,则可尝试将该热点数据设置为永不过期。若缓存的数据更新不频繁,且缓存刷新的整个流程耗时较少的情况下,则可以采用基于 Redis、zookeeper 等分布式中间件的分布式互斥锁,或者本地互斥锁以保证仅少量的请求能请求数据库并重新构建缓存,其余线程则在锁释放后能访问到新缓存。若缓存的数据更新频繁或者在缓存刷新的流程耗时较长的情况下,可以利用定时线程在缓存过期前主动地重新构建缓存或者延后缓存的过期时间,以保证所有的请求能一直访问到对应的缓存。 什么是Redis中的BigKey
在Redis中,一个字符串最大512MB,一个二级数据结构(例如hash、list、set、zset)可以存储大约40亿个(2^32-1)个元素,但实际上中如果下面两种情况,我就会认为它是bigkey。
在Redis开发规范中,string类型控制在10KB以内,hash、list、set、zset元素个数不要超过5000。
Redis的BigKey的危害操作导致超时阻塞:由于Redis单线程的特性,操作bigkey的通常比较耗时,也就意味着阻塞Redis可能性越大,这样会造成客户端阻塞或者引起故障切换,它们通常出现在慢查询中。
查询导致网络阻塞: bigkey也就意味着每次获取要产生的网络流量较大,假设一个bigkey为1MB,客户端每秒访问量为1000,那么每秒产生1000MB的流量,对于普通的千兆网卡(按照字节算是128MB/s)的服务器来说简直是灭顶之灾,而且一般服务器会采用单机多实例的方式来部署,也就是说一个bigkey可能会对其他实例造成影响,其后果不堪设想。
过期删除导致阻塞:有个bigkey,它安分守己(只执行简单的命令,例如hget、lpop、zscore等),但它设置了过期时间,当它过期后,会被删除,如果没有使用Redis 4.0的过期异步删除(lazyfree-lazy-expire yes),就会存在阻塞Redis的可能性,而且这个过期删除不会从主节点的慢查询发现(因为这个删除不是客户端产生的,是内部循环事件,可以从latency命令中获取或者从slave节点慢查询发现)
Redis的Bigkey怎么产生?这个问题就可以作为面试的时候,你在使用Redis中出现的问题
一般来说,bigkey的产生都是由于程序设计不当,或者对于数据规模预料不清楚造成的,来看几个:
(1) 社交类:粉丝列表,如果某些明星或者大v不精心设计下,必是bigkey。
(2) 统计类:例如按天存储某项功能或者网站的用户集合,除非没几个人用,否则必是bigkey。
(3) 缓存类:将数据从数据库load出来序列化放到Redis里,这个方式非常常用,但有两个地方需要注意,第一,是不是有必要把所有字段都缓存,第二,有没有相关关联的数据。
例如我之前遇到过一个例子,该同学将某明星一个专辑下所有视频信息都缓存一个巨大的json中,造成这个json达到6MB,后来这个明星发了一个官宣。。。这个我就不多说了,领盒饭去吧。
怎么解决可以看一下<老生常谈的bigkey.md>
后,会被删除,如果没有使用Redis 4.0的过期异步删除(lazyfree-lazy-expire yes),就会存在阻塞Redis的可能性,而且这个过期删除不会从主节点的慢查询发现(因为这个删除不是客户端产生的,是内部循环事件,可以从latency命令中获取或者从slave节点慢查询发现)
Redis的Bigkey怎么产生?这个问题就可以作为面试的时候,你在使用Redis中出现的问题
一般来说,bigkey的产生都是由于程序设计不当,或者对于数据规模预料不清楚造成的,来看几个:
(1) 社交类:粉丝列表,如果某些明星或者大v不精心设计下,必是bigkey。
(2) 统计类:例如按天存储某项功能或者网站的用户集合,除非没几个人用,否则必是bigkey。
(3) 缓存类:将数据从数据库load出来序列化放到Redis里,这个方式非常常用,但有两个地方需要注意,第一,是不是有必要把所有字段都缓存,第二,有没有相关关联的数据。
例如我之前遇到过一个例子,该同学将某明星一个专辑下所有视频信息都缓存一个巨大的json中,造成这个json达到6MB,后来这个明星发了一个官宣。。。这个我就不多说了,领盒饭去吧。
怎么解决可以看一下<老生常谈的bigkey.md>