1. Http协议中,get与post的区别。
(1)get请求一般用于获取数据,post请求一般用于需要发数据到后台时使用
(2)get请求的参数,会放在url上,所以安全性,隐私性会比较差,post请求的参数会放在request.body中,比较的安全
(3)get请求不受到刷新,回退的影响,post请求则会在网页回退或者刷新后,重新进行发送
(4)get请求会被缓存,post请求不会被缓存
(5)get请求会被记录在浏览器的历史记录中,post请求则不会被记录
(6)get请求可以被收藏为标签,post不能被收藏为标签
(7)get请求只能进行url编码,post请求则支持多种编码
(8)get请求通常通过url地址栏请求,post通常通过表单发送数据请求
(9) post请求url长度理论上没有限制,get请求有限制[不同的浏览器不一样]
2.什么是IOC,有什么好处?底层用什么技术实现?
Ioc—Inversion of Control,即“控制反转”,一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
IOC的优点:
第一,资源集中管理,实现资源的可配置和易管理。
第二,降低了使用资源双方的依赖程度,也就是我们说的耦合度。IOC底层原理使用的技术
xml配置文件
工厂模式
反射
3.什么是AOP? 使用场景是什么?AOP底层技术是什么?两种代理模式的区别是什么?
AOP面向切面编程
定义:通过预编译方式和运行期动态代理实现,在不修改源代码的情况下,给程序动态统一添加功能的一种技术。是spring框架的一个重要内容,是OOP(面向对象编程)的衍生模范。
AOP的作用:
AOP可以做到在程序的运行期间, 不修改业务代码的情况下对方法进行功能的增强.
AOP的使用场景:
可以使用AOP进行程序运行时的权限验证.
使用AOP添加日志输出功能,避免因为日志模块修改API导致修改起来很复杂的问题。
监听一些重要的生命周期的运行,并输出日志
AOP的优势:
- AOP可以减少重复的代码
- AOP可以在很大程度上提高开发效率
- AOP编写出来的代码, 可以很方便的进行维护
AOP底层技术:
AOP的底层是通过spring提供的动态代理技术实现的. 在程序的运行期间, spring动态生成代理对象, 代理对象的方法在执行时就可以进行增强功能的介入, 从而完成目标对象方法的功能增强.
JDK动态代理和CGLIB动态代理的区别?
(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类
(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
因为是继承,所以该类或方法最好不要声明成final
4.Spring中的bean的生命周期是什么?
- 实例化 Instantiation
- 属性赋值 Populate
- 初始化 Initialization
- 销毁 Destruction
实例化一个Bean,也就是我们通常说的new
按照Spring上下文对实例化的Bean进行配置,也就是IOC注入
如果这个Bean实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的是Spring配置文件中Bean的ID
如果这个Bean实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(),传递的是Spring工厂本身(可以用这个方法获取到其他Bean)
如果这个Bean实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文,该方式同样可以实现步骤4,但比4更好,以为ApplicationContext是BeanFactory的子接口,有更多的实现方法
如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用After方法,也可用于内存或缓存技术
如果这个Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法
如果这个Bean关联了BeanPostProcessor接口,将会调用postAfterInitialization(Object obj, String s)方法
注意:以上工作完成以后就可以用这个Bean了,那这个Bean是一个single的,所以一般情况下我们调用同一个ID的Bean会是在内容地址相同的实例
当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean接口,会调用其实现的destroy方法
最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法
5. Spring的依赖注入有哪些方式?
什么是依赖注入
依赖 : 指Bean对象的创建依赖于容器 .
注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配 .
由容器动态的将某个依赖关系注入到组件之中
依赖注入的类型有三类
- 基本数据类型和String类型
- 其他bean类型(在配置文件中或者注解配置过的bean)
- 复杂类型/集合类型
注入方式
- **通过构造函数注入(**前提:构造函数注入的属性最好不经常发生改变,类中提供了有参的构造函数.)
- 使用属性的setter方法注入(属性注入即通过setXxx()方法注入Bean的属性值或依赖对象,由于属性注入方式具有可选择性和灵活性高的优点,因此属性注入是实际应用中最常采用的注入方式)
- 注解注入(使用注解注入依赖对象不用再在代码中写依赖对象的setter方法或者该类的构造方法,并且不用再配置文件中配置大量的依赖对象,使代码更加简洁,清晰,易于维护)
- 接口注入(接口注入模式因为具备侵入性,它要求组件必须与特定的接口相关联,因此并不被看好,实际使用有限)
6.spring如何实现事务管理?
- 编程式事务管理:通过编程的方式管理事务,这种方式带来了很大的灵活性,但很难维护。
- 声明式事务管理:将事务管理和业务代码分离。你只需要通过注解或者XML配置管理事务
7. 描述session与cookie.
- cookie数据存放在客户的浏览器上,session数据放在服务器上.
- cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗考虑到安全应当使用session。
- 设置cookie时间可以使cookie过期。但是使用session-destory(),我们将会销毁会话。
- session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能考虑到减轻服务器性能方面,应当使用cookie。
- 单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。(Session对象没有对存储的数据量的限制,其中可以保存更为复杂的数据类型)
8. 描述SpringBoot的优势
- 快速构建项目。
- 对主流开发框架的无配置集成;与其Spring生态系统(如Spring JDBC,Spring ORM,Spring Data,Spring Security等)集成非常容易
- 项目可独立运行,无须外部依赖Servlet容器。
- 提供运行时的应用监控。
- 极大地提高了开发、部署效率。
- 去除了大量的xml配置文件
- 简化复杂的依赖管理
- 自动化配置,简化编码
9.SpringBoot有哪些常用的注解?
@SpringBootApplication
申明让spring boot自动给程序进行必要的配置
@RestController
标注控制层组件
@Configuration
指出该类是Bean配置的信息源
@PathVariable
获取参数@Autowired自动导入。
@Service 标注业务层组件
10. 什么是Restful 编程风格?
(1)每一个URI代表一种资源;
(2)客户端和服务器之间,传递这种资源的某种表现层;
(3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现”表现层状态转化”。
11.Redis中有哪些数据类型?
五种常用的数据类型:
1、String:String是最常用的一种数据类型,普通的key- value 存储都可以归为此类。其中Value既可以是数字也可以是字符串。使用场景:常规key-value缓存应用。常规计数: 微博数, 粉丝数。
2、Hash:Hash 是一个键值(key => value)对集合。Redishash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象,并且可以像数据库中update一个属性一样只修改某一项属性值。
3、Set:Set是一个无序不重复集合,即Key-Set。此外还提供了交集、并集等一系列直接操作集合的方法,对于求共同好友、共同关注什么的功能实现特别方便。
4、List:List是一个有序可重复的集合,其遵循FIFO的原则,底层是依赖双向链表实现的,因此支持正向、反向双重查找。通过List,我们可以很方面的获得类似于最新回复这类的功能实现。
5、SortedSet:有序集合,类似于java中的TreeSet,是Set的可排序版。此外还支持优先级排序,维护了一个score的参数来实现。适用于排行榜和带权重的消息队列等场景。
三种特殊的数据类型:
1、Bitmap:位图,Bitmap想象成一个以位为单位数组,数组中的每个单元只能存0或者1,数组的下标在Bitmap中叫做偏移量。使用Bitmap实现统计功能,更省空间。如果只需要统计数据的二值状态,例如商品有没有、用户在不在等,就可以使用 Bitmap,因为它只用一个 bit 位就能表示 0 或 1。
2、Hyperloglog。统计不重复数据, 用于大数据基数统计
3、Geospatial :主要用于存储地理位置信息,并对存储的信息进行操作,适用场景如朋友的定位、附近的人、打车距离计算
12. 你在哪些业务中使用过Redis?(使用场景)
秒杀
13.Redis的优缺点是什么?
优点:
- 读写性能极高, Redis能读的速度是110000次/s,写的速度是81000次/s。
- 支持数据持久化,支持AOF和RDB两种持久化方式。
- 支持事务, Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
- 数据结构丰富,除了支持string类型的value外,还支持hash、set、zset、list等数据结构。
- 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。
- 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等特性。
缺点:
- 数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
- 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
14. Redis 持久化有哪些?有什么区别?
- RDB是一种快照存储持久化方式,具体就是将Redis某一时刻的内存数据保存到硬盘的文件当中,默认保存的文件名为dump.rdb,而在Redis服务器启动时,会重新加载dump.rdb文件的数据到内存当中恢复数据。
- **以日志的形式来记录每个写操作(增量保存),将Redis执行过的所有写指令记录下来(读操作不记录)**, 只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作.
- RDB AOF 启动优先级 低 高 体积 小 大 恢复速度 快 慢 数据安全性 丢失若干时间内的数据 根据策略决定 总结:
RDB,简而言之,就是在不同的时间点,将redis存储的数据生成快照并存储到磁盘等介质上;
AOF,则是换了一个角度来实现持久化,那就是将redis执行过的所有写指令记录下来,在下次redis重新启动时,只要把这些写指令从前到后再重复执行一遍,就可以实现数据恢复了。
15.单线程的redis为什么这么快?
- 内存存储:Redis是使用内存(in-memeroy)存储,没有磁盘IO上的开销
- 单线程实现:Redis使用单个线程处理请求,避免了多个线程之间线程切换和锁资源争用的开销
- 非阻塞IO:Redis使用多路复用IO技术,在poll,epool,kqueue选择最优IO实现
- 优化的数据结构:Redis有诸多可以直接应用的优化数据结构的实现,应用层可以直接使用原生的数据结构提升性能
16.redis的过期策略以及内存淘汰机制
定期删除
定期删除指的是Redis默认每隔100ms就随机抽取一些设置了过期时间的key,检测这些key是否过期,如果过期了就将其删掉。
惰性删除
惰性删除不再是Redis去主动删除,而是在客户端要获取某个key的时候,Redis会先去检测一下这个key是否已经过期,如果没有过期则返回给客户端,如果已经过期了,那么Redis会删除这个key,不会返回给客户端。
内存淘汰机制
Redis在使用内存达到某个阈值(通过maxmemory配置)的时候,就会触发内存淘汰机制,选取一些key来删除。
allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。
volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
17. 如何应对缓存穿透和缓存雪崩问题
什么是缓存击穿?
缓存击穿跟缓存雪崩有点类似,缓存雪崩是大规模的key失效,而缓存击穿是某个热点的key失效,大并发集中对其进行请求,就会造成大量请求读缓存没读到数据,从而导致高并发访问数据库,引起数据库压力剧增。这种现象就叫做缓存击穿。
从两个方面解决,第一是否可以考虑热点key不设置过期时间,第二是否可以考虑降低打在数据库上的请求数量。
解决方案:
- 在缓存失效后,通过互斥锁或者队列来控制读数据写缓存的线程数量,比如某个key只允许一个线程查询数据和写缓存,其他线程等待。这种方式会阻塞其他的线程,此时系统的吞吐量会下降
- 热点数据缓存永远不过期。永不过期实际包含两层意思:
- 物理不过期,针对热点key不设置过期时间
- 逻辑过期,把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建
什么是缓存穿透?
缓存穿透是指用户请求的数据在缓存中不存在即没有命中,同时在数据库中也不存在,导致用户每次请求该数据都要去数据库中查询一遍。如果有恶意攻击者不断请求系统中不存在的数据,会导致短时间大量请求落在数据库上,造成数据库压力过大,甚至导致数据库承受不住而宕机崩溃。
缓存穿透的关键在于在Redis中查不到key值,它和缓存击穿的根本区别在于传进来的key在Redis中是不存在的。假如有黑客传进大量的不存在的key,那么大量的请求打在数据库上是很致命的问题,所以在日常开发中要对参数做好校验,一些非法的参数,不可能存在的key就直接返回错误提示。
解决方法:
- 将无效的key存放进Redis中:
当出现Redis查不到数据,数据库也查不到数据的情况,我们就把这个key保存到Redis中,设置value=”null”,并设置其过期时间极短,后面再出现查询这个key的请求的时候,直接返回null,就不需要再查询数据库了。但这种处理方式是有问题的,假如传进来的这个不存在的Key值每次都是随机的,那存进Redis也没有意义。
- 使用布隆过滤器:
如果布隆过滤器判定某个 key 不存在布隆过滤器中,那么就一定不存在,如果判定某个 key 存在,那么很大可能是存在(存在一定的误判率)。于是我们可以在缓存之前再加一个布隆过滤器,将数据库中的所有key都存储在布隆过滤器中,在查询Redis前先去布隆过滤器查询 key 是否存在,如果不存在就直接返回,不让其访问数据库,从而避免了对底层存储系统的查询压力。
如何选择:针对一些恶意攻击,攻击带过来的大量key是随机,那么我们采用第一种方案就会缓存大量不存在key的数据。那么这种方案就不合适了,我们可以先对使用布隆过滤器方案进行过滤掉这些key。所以,针对这种key异常多、请求重复率比较低的数据,优先使用第二种方案直接过滤掉。而对于空数据的key有限的,重复率比较高的,则可优先采用第一种方式进行缓存。
什么是缓存雪崩?
如果缓存在某一个时刻出现大规模的key失效,那么就会导致大量的请求打在了数据库上面,导致数据库压力巨大,如果在高并发的情况下,可能瞬间就会导致数据库宕机。这时候如果运维马上又重启数据库,马上又会有新的流量把数据库打死。这就是缓存雪崩。
造成缓存雪崩的关键在于同一时间的大规模的key失效,主要有两种可能:第一种是Redis宕机,第二种可能就是采用了相同的过期时间。
解决方案:
1、事前:
- 均匀过期:设置不同的过期时间,让缓存失效的时间尽量均匀,避免相同的过期时间导致缓存雪崩,造成大量数据库的访问。如把每个Key的失效时间都加个随机值,
setRedis(Key,value,time + Math.random() * 10000);
,保证数据不会在同一时间大面积失效。 - 分级缓存:第一级缓存失效的基础上,访问二级缓存,每一级缓存的失效时间都不同。
- 热点数据缓存永远不过期。永不过期实际包含两层意思:
- 物理不过期,针对热点key不设置过期时间
- 逻辑过期,把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建
- 保证Redis缓存的高可用,防止Redis宕机导致缓存雪崩的问题。可以使用 主从+ 哨兵,Redis集群来避免 Redis 全盘崩溃的情况。
2、事中:
- 互斥锁:在缓存失效后,通过互斥锁或者队列来控制读数据写缓存的线程数量,比如某个key只允许一个线程查询数据和写缓存,其他线程等待。这种方式会阻塞其他的线程,此时系统的吞吐量会下降
- 使用熔断机制,限流降级。当流量达到一定的阈值,直接返回“系统拥挤”之类的提示,防止过多的请求打在数据库上将数据库击垮,至少能保证一部分用户是可以正常使用,其他用户多刷新几次也能得到结果。
3、事后:
开启Redis持久化机制,尽快恢复缓存数据,一旦重启,就能从磁盘上自动加载数据恢复内存中的数据。
18. 如何解决 redis 的并发竞争key问题
使用分布式锁+时间戳.
1.整体技术方案
这种情况,主要是准备一个分布式锁,大家去抢锁,抢到锁就做set操作。加锁的目的实际上就是把并行读写改成串行读写的方式,从而来避免资源竞争。
2.Redis分布式锁的实现
主要用到的redis函数是setnx()用SETNX实现分布式锁
利用SETNX非常简单地实现分布式锁。例如:某客户端要获得一个名字youzhi的锁,客户端使用下面的命令进行获取:
SETNX lock.youzhi
如返回1,则该客户端获得锁,把lock.youzhi的键值设置为时间值表示该键已被锁定,该客户端最后可以通过DEL lock.foo来释放该锁。
如返回0,表明该锁已被其他客户端取得,这时我们可以先返回或进行重试等对方完成或等待锁超时。3.时间戳
由于上面举的例子,要求key的操作需要顺序执行,所以需要保存一个时间戳判断set顺序。系统A key 1 {ValueA 7:00}
系统B key 1 { ValueB 7:05}
假设系统B先抢到锁,将key1设置为{ValueB 7:05}。接下来系统A抢到锁,发现自己的key1的时间戳早于缓存中的时间戳(7:00<7:05),那就不做set操作了。
分布式锁
因为传统的加锁的做法(如java的synchronized和Lock)这里没用,只适合单点。因为这是分布式环境,需要的是分布式锁。
当然,分布式锁可以基于很多种方式实现,比如zookeeper、redis等,不管哪种方式实现,基本原理是不变的:用一个状态值表示锁,对锁的占用和释放通过状态值来标识。
19. MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据
保留热点数据:对于保留 Redis 热点数据来说,我们可以使用 Redis 的内存淘汰策略来实现,可以使用allkeys-lru淘汰策略,该淘汰策略是从 Redis 的数据中挑选最近最少使用的数据删除,这样频繁被访问的数据就可以保留下来了。
20.ES
2.ES工作原理
第一步:查询分析
正常情况下用户输入正确的查询,比如搜索“里约奥运会”这个关键词,用户输入正确完成一次搜索,但是搜索通常都是全开放的,任何的用户输入都是有可能的,很大一部分还是非常口语化和个性化的,有时候还会存在拼写错误,用户不小心把“淘宝”打成“涛宝”,这时候需要用自然语言处理技术来做拼写纠错等处理,以正确理解用户需求。第二步:分词技术
这一步利用自然语言处理技术将用户输入的查询语句进行分词,如标准分词会把“lucene全文检索框架”分成 lucene | 全 | 文|检|索|框|架|, IK分词会分成: lucene|全文|检索|框架|,还有简单分词等多种分词方法。第三步:关键词检索
提交关键词后在倒排索引库中进行匹配,倒排索引就是关键词和文档之间的对应关系,就像给文档贴上标签。比如在文档集中含有 “lucene” 关键词的有文档1 、文档 6、文档9,含有 “全文检索” 关键词的有文档1 、文档6 那么做与运算,同时含有 “lucene” 和 “全文检索” 的文档就是文档1和文档6,在实际的搜索中会有更复杂的文档匹配模型。第四步:搜索排序
对多个相关文档进行相关度计算、排序,返回给用户检索结果
ElasticSearch 的实现原理主要分为以下几个步骤,首先用户将数据提交到Elastic Search 数据库中,再通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据,当用户搜索数据时候,再根据权重将结果排名,打分,再将返回结果呈现给用户
红黑树优点:
- 红黑树是效率相对较高的当我们插入和删除数据相对频繁的时候
- 红黑树是自我平衡的所有操作的复杂度最多是O(logn)
- 不管怎么变化,只有红黑两个常数
缺点:树的高度随着数据量增加而增加,IO代价高。
RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。
削峰:用消息队列来缓冲瞬时流量,把同步的直接调用转换成异步的间接推送,中间通过一个队列在一端承接瞬时的流量洪峰,在另一端平滑地将消息推送出去