文章目录
- 面试专题-java高级篇
- 1. JVM
- 有做过jvm的调优吗?常用的jvm参数调优有哪些?
- 如果jvm持续一段时间频繁的发生Young GC (轻GC) 可能原因有哪些?
- 2. Mysql
- 2.1. 基本功(见为知笔记)
- 2.2. 什么是索引
- 2.3. 索引的优劣势
- 2.4. MySQL的索引结构
- 2.4.1. B-Tree索引
- 2.4.2. B+Tree索引
- 2.4.3. 聚簇索引与非聚簇索引
- 2.4.4. MySQL回表
- 2.5. 索引的使用场景
- 2.6. SQL语句如何优化
- 2.6.1. 单表优化
- 2.6.2. 关联查询优化
- 2.6.3. 子查询优化
- 2.6.4. 排序优化
- 2.6.5. 分组优化
- 2.6.6. 覆盖索引
- 2.7. 如何批量将百万数据快速导入数据库
- 3. Redis
- 3.1.简单介绍一下redis
- 3.2. 单线程的redis为什么读写速度快?
- 3.3. redis为什么是单线程的?
- 3.4.redis单线程如何解决keys 模糊匹配的查询阻塞问题?
- 3.5. redis服务器的的内存是多大?
- 3.6. 为什么Redis的操作是原子性的,怎么保证原子性的?
- 3.7. 对redis的持久化了解不?
- redis的过期策略以及内存淘汰机制有了解过吗
- 3.8. 做过redis的集群吗?你们做集群的时候搭建了几台,都是怎么搭建的?
- 3.8.1. 下载Redis源码,编译安装
- 3.8.2. 编写配置文件
- 3.8.3. 启动Redis节点
- 3.8.4. 创建集群
- 3.9. 说说Redis哈希槽的概念?
- 3.10. 什么是redis的缓存穿透?如何防止穿透?
- 3.11. 什么是redis的缓存雪崩?如何防止?
- 3.12. 什么是redis的缓存击穿?如何防止?
- 4. Kafka
- 4.1 什么是Kafka?它的特点和使用场景是什么?
- 4.2 Kafka的消息有哪些主要组件?
- 4.3 消息的保留策略有哪些?它们分别有什么作用?
- 4.4 Kafka如何保证消费的顺序和可靠性?
- 4.5 Kafka的缺点是什么?
- 4.6 Kafka 实际生产环境中是否会存在消息积压的问题,如果有,该如何处理?
- 4.7 Kafka如何防止消息不丢失?请具体说一下
- 4.6 Kafka 实际生产环境中是否会存在消息积压的问题,如果有,该如何处理?
- 4.7 Kafka如何防止消息不丢失?请具体说一下
面试专题-java高级篇
1. JVM
有做过jvm的调优吗?常用的jvm参数调优有哪些?
回答话术: jvm参数之前在工作的时候也偶尔做过,但是也不能完全记得住,每次都是在需要的时候去查询文档 一般情况下都是使用默认值,只有在真正需要调优的时候会去重新设置值去覆盖默认值…停顿几秒思考
根据我的回忆,jvm参数分为三种
标准参数: 主要用于查看一些基本信息 比如jvm版本号
X参数: 用于设置内存大小 基本都是传给 JVM 的,默认 JVM 实现这些参数的功能,但是并不保证所有 JVM 实 现都满足,且不保证向后兼容 稳定性好
XX参数 用于设置内存大小 专门用于控制 JVM 的行为,跟具体的 JVM 实现有关,随时可能会在下个版本取消
稳定性差一些
接下来举例说明几个常用的参数
-Xms 内存的初始值大小 以M(兆)为单位 默认为系统内存的1/64
-Xmx 内存的最大值 也是以M(兆)为单位 最大值为系统内存的1/4
一般情况下,会将如下两个参数设置为相同的值,可以避免jvm内存的自动扩展,因为当堆内存大小发生扩展的时候,就会发生内存的抖动,会影响到程序的稳定性
-Xmn 用于设置新生代的内存大小,一般设置为堆空间的1/3或者1/4,因为新生代大的话,老年代就会变小
-Xss 用于设置每个线程的虚拟机栈的大小也即堆栈的大小
-XX -XX:+UseG1GC 设置垃圾回收器
当然还有很多的参数,一般都是需要的时候会按照文档就行设置,具体的就记不住那么多了…
-Xmx1024M 最大堆内存
-Xms1024M 初始化堆内存,正常和最大堆内存相同,减少动态改变的内存损耗
-Xmn384M 年轻代内存-XX:+PrintGCDetails 打印gc信息,可参考gc的比例进行调优
-XX:+UseConcMarkSweepGC 老年代使用cms,标记-清除算法会产生碎片
-XX:+UseParNewGC 年轻代使用并行收集器
如果jvm持续一段时间频繁的发生Young GC (轻GC) 可能原因有哪些?
首先频繁的YGC 说明会频繁的创建对象并立马被回收了 我想造成这个问题的原因可能有两点
第一点: for循环内部 频繁的创建局部对象从而导致频繁的GC 当然也有可能是死循环导致的
第二点: 年轻代内存大小设置不足,无法满足程序逻辑的需要从而导致频繁GC
为了解决这个问题,要么就是减少对象的创建,当然这个要修改程序,难度和周期都比较长一些,要么就是增大年轻代的内存大小,这样可以更快的解决这个问题.当然了,最可靠的还是修改代码,通过GC日志定位问题
2. Mysql
2.1. 基本功(见为知笔记)
2.2. 什么是索引
MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。可以得到索引的本质:索引是数据结构。
索引的目的在于提高查询效率,可以类比字典
2.3. 索引的优劣势
优势:
- 类似大学图书馆建书目索引,提高数据检索的效率,降低数据库的IO成本。
- 通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗。
劣势:
- 虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件。每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息
- 实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间的
2.4. MySQL的索引结构
2.4.1. B-Tree索引
【初始化介绍】
一颗b树,包含多个磁盘块,可以看到每个磁盘块包含几个键值(表中记录的主键)、数据(表中除主键外的数据)和指针(指向下一个磁盘块的指针)如磁盘块1包含键值17和35,包含指针P1、P2、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非叶子节点不存储真实的数据,只存储指引搜索方向的键值信息,如17、35并不真实存在于数据表中。
【查找过程】
如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间因为非常短(相比磁盘的IO)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。
真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。
2.4.2. B+Tree索引
通常在B+Tree上有两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点,而且所有叶子节点(即数据节点)之间是一种链式环结构。因此可以对B+Tree进行两种查找运算:一种是对于主键的范围查找和分页查找,另一种是从根节点开始,进行随机查找。
B+Tree相对于B-Tree有几点不同:
-
非叶子节点只存储键值信息。
-
所有叶子节点之间都有一个链指针。
-
数据记录都存放在叶子节点中。
1)B+树的磁盘读写代价更低
B+树的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B 树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了;
2)B+树查询效率更加稳定
由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当;
3)B+树便于范围查询(最重要的原因,范围查找是数据库的常态)
B树在提高了IO性能的同时并没有解决元素遍历时候效率低下的问题,正是为了解决这个问题,B+树应用而生。B+树只需要去遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作或者说效率太低。
2.4.3. 聚簇索引与非聚簇索引
聚簇索引:将数据存储与索引放到了一块,找到索引也就找到了数据
非聚簇索引:将数据存储与索引分开结构,索引结构的叶子节点指向了数据的对应行,myisam通过key_buffer把索引先缓存到内存中,当需要访问数据时(通过索引访问数据),在内存中直接搜索索引,然后通过索引找到磁盘相应数据这也就是为什么索引不在key buffer命中时,速度慢的原因
innodb中,在聚簇索引之上创建的索引称之为辅助索引,辅助索引访问数据总是需要二次查找,非聚簇索引都是辅助索引。辅助索引叶子节点存储的不再是行的物理位置,而是主键值。
InnoDB使用的是聚簇索引,将主键组织到一棵B+树中,而行数据就储存在叶子节点上,若使用"where id = 14"这样的条件查找主键,则按照B+树的检索算法即可查找到对应的叶节点,之后获得行数据。若对Name列进行条件搜索,则需要两个步骤:第一步在辅助索引B+树中检索Name,到达其叶子节点获取对应的主键。第二步使用主键在主索引B+树种再执行一次B+树检索操作,最终到达叶子节点即可获取整行数据。(重点在于通过其他键需要建立辅助索引)
2.4.4. MySQL回表
Mysql回表指的是在InnoDB存储引擎下,二级索引查询到的索引列,如果需要查找所有列的数据,则需要到主键索引里面去取出数据。这个过程就称为回表。因为行的数据都是存在主键B+tree的叶子节点里面,二级索引的B+树叶子节点都是存放的(索引列,主键)
2.5. 索引的使用场景
哪些情况需要创建索引
主键自动建立唯一索引
频繁作为查询条件的字段应该创建索引
查询中与其它表关联的字段,外键关系建立索引
单键/组合索引的选择问题, 组合索引性价比更高
查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
查询中统计或者分组字段
哪些情况不要创建索引
表记录太少
经常增删改的表或者字段。
Why:提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件
Where条件里用不到的字段不创建索引
过滤性不好的不适合建索引
2.6. SQL语句如何优化
2.6.1. 单表优化
- 不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描
- like以通配符开头(‘%abc…’)mysql索引失效会变成全表扫描的操作
- mysql 在使用**不等于(!=或者<>)**的时候无法使用索引会导致全表扫描
- is not null 也无法使用索引,但是is null是可以使用索引的
- 字符串不加单引号索引失效
组合索引
- 全值匹配我最爱
- 符合最左原则:不跳过索引中的列。
- 如果where条件中是OR关系,加索引不起作用
- 范围查询右边的索引条件不走索引
总结
对于单键索引,尽量选择针对当前query过滤性更好的索引在选择组合索引的时候,当前Query中过滤性最好的字段在索引字段顺序中,位置越靠前越好。在选择组合索引的时候,尽量选择可以能够包含当前query中的where字句中更多字段的索引在选择组合索引的时候,如果某个字段可能出现范围查询时,尽量把这个字段放在索引次序的最后面书写sql语句时,尽量避免造成索引失效的情况
2.6.2. 关联查询优化
- 保证被驱动表的join字段已经被索引
- left/right join 时,选择小表作为驱动表,大表作为被驱动表。
- inner join 时,mysql会自己帮你把小结果集的表选为驱动表。
- 子查询尽量不要放在被驱动表,有可能使用不到索引。
- 能够直接多表关联的尽量直接关联,不用子查询。
2.6.3. 子查询优化
尽量不要使用not in 或者 not exists
2.6.4. 排序优化
ORDER BY子句,尽量使用Index方式排序,避免使用FileSort方式排序
排序顺序要保持一致
2.6.5. 分组优化
group by 使用索引的原则几乎跟order by一致 ,唯一区别是groupby 即使没有过滤条件用到索引,也可以直接使用索引。
2.6.6. 覆盖索引
最后使用索引的手段:覆盖索引
什么是覆盖索引?简单说就是,select 到 from 之间查询的列 <=使用的索引列+主键
2.7. 如何批量将百万数据快速导入数据库
要批量将百万数据快速导入数据库,可以考虑以下几个步骤:
- 选择合适的数据库:选择能够支持快速批量导入的数据库。例如,MySQL和PostgreSQL都提供了快速导入数据的功能。
- 准备数据:将数据准备成符合数据库要求的格式,例如使用CSV格式或者数据库支持的其他格式。
- 使用命令行工具导入数据:许多数据库都提供了命令行工具来导入数据,这些工具可以快速地导入大量数据。例如,可以使用MySQL的"LOAD DATA INFILE"命令或者PostgreSQL的"COPY"命令。
- 使用批量插入API:许多数据库还提供了批量插入API,这些API可以在代码中使用,可以快速地将大量数据批量插入数据库。例如,MySQL提供了"LOAD DATA LOCAL INFILE"命令和"INSERT INTO … VALUES"语句,而PostgreSQL提供了"pg_bulkload"工具和"INSERT INTO … VALUES"语句。
- 调整数据库配置:在导入大量数据时,可能需要调整数据库的配置以提高性能。例如,可以增加数据库缓存大小,调整日志记录级别等。
需要注意的是,在导入大量数据时,可能会影响数据库的性能和可用性。因此,在导入数据之前应该备份数据库,并且在导入过程中应该监视数据库的状态,以确保不会出现意外的问题。
3. Redis
3.1.简单介绍一下redis
(1)redis是一个key-value类型的非关系型数据库,基于内存也可持久化的数据库,相对于关系型数据库(数据主要存在硬盘中),性能高,因此我们一般用redis来做缓存使用;并且redis支持丰富的数据类型,比较容易解决各种问题
类型 | 底层数据结构/介绍 | 使用场景 |
---|---|---|
string | 简单动态字符串(Simple Dynamic string 缩写SDS).是可以修改的字符串,内部结构实现上类似java中的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配.需要注意的是字符串最大长度为512M | 项目中我们主要利用单点登录中的token用string类型来存储;商品详情 |
hash | Hash类型第一段数据结构有两种:ziplist(压缩列表),hashtable(哈希表).当field-value长度较短且个数较少时,使用ziplist,否则使用HashTable | Hash类型中的key是string类型,value又是一个map(key-value),针对这种数据特性,比较适合存储对象,在我们项目中由于购物车是用redis来存储的,因此选择redis的散列(hash)来存储; |
list | 单键多值,底层是快速双向链表quicklist,在列表元素较少的情况下会使用一块连续的内存存储,结构为ziplist即压缩列表,它将所有的元素紧挨着一起存储,分配的是一块连续的内存.当数据量比较多的时候才会改成quicklist,因为普通的链表需要的附加指针空间太大,浪费空间,eg:列表中存储的只是int类型的数据,结构上还需要两个额外的指针prev与next.redis将链表和ziplist组合起来组成quicklist,即将多个ziplist使用双向指针串起来使用,既满足了快速插入删除性能,又不会出现太大的空间冗余 | List类型是按照插入顺序的字符串链表(双向链表),主要命令是LPOP和RPUSH,能够支持反向查找和遍历,如果使用的话主要存储商品评论列表,key是该商品的ID,value是商品评论信息列表;消息队列 |
set | set数据结构是dict字典,字典是用哈希表实现的,java中HashSet内部使用的是HashMap,只不过所有的value都指向同一个对象.Redis中的set也是一样的,他的内部结构也使用hash结构,所有的value都指向同一个内部值 | 可以基于 Set 玩儿交集、并集、差集的操作,比如交集吧,我们可以把两个人的好友列表整一个交集,看看俩人的共同好友是谁? |
zset | zset底层使用了两个数据结构 (1)hash,hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到对应的score的值 (2)跳跃表,跳跃表的目的在于给元素value排序,根据score的范围获取元素列表 | zset(sorted set)类型和set类型基本是一致的,不同的是zset这种类型会给每个元素关联一个double类型的分数(score),这样就可以为成员排序,并且插入是有序的。这种数据类型如果使用的话主要用来统计商品的销售排行榜,比如:items:sellsort 10 1001 20 1002 这个代表编号是1001的商品销售数量为10,编号为1002的商品销售数量为20/附件的人 |
Bitmaps | bitmaps可以实现对位的操作,节约内存空间(前提是数据量大) (1) bigmaps本身不是一种数据类型,实际上是字符串,但是它可以对字符串进行位运算 (2)bitmaps单独提供了一套命令,可以把bitmaps理解成一个以位为单位的数组,数组的每一个单元只能存储0或者1,数组的小标在bitmaps中叫做偏移量 | 统计网站活跃用户 解决redis随机穿透攻击 |
HyperLogLog | 统计uv,独立ip数,搜索记录数等需要去重和计数的问题如何解决?这种求集合中不重复元素个数的问题成为基数问题.常规的解决办法包括mysql中的distinct与redis的hash set等方案精确计算,但是随着数据不断增加,导致空间越来越大,对于非常大的数据集是不切实际的.该数据类型可以通过降低精度来平衡存储空间,是用来做基数(集合中不重复元素的个数)统计的算法 | 计算基数的应用场景 网站的uv 固定IP数 搜索记录数 |
Geospatial | 地理信息的缩写,该类型,就是元素的二维坐标,在地图上基数经纬度,redis基于该类型,提供了经纬度设置,查询,范围查询,距离查询 经纬度hash等常见操作 |
3.2. 单线程的redis为什么读写速度快?
- 纯内存操作
- 单线程操作,避免了频繁的上下文切换
- 采用了非阻塞I/O多路复用机制
3.3. redis为什么是单线程的?
官方FAQ表示,因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了Redis利用队列技术将并发访问变为串行访问
1)绝大部分请求是纯粹的内存操作
2)采用单线程,避免了不必要的上下文切换和竞争条件
3.4.redis单线程如何解决keys 模糊匹配的查询阻塞问题?
由于Redis是单线程,keys命令是以阻塞的方式执行的,keys是以遍历的方式实现的复杂度是 O(n),Redis库中的key越多,查找实现代价越大,产生的阻塞时间越长
为了解决Keys命令的痛点,Redis2.8版本中加入了Scan指令,特点是迭代遍历,并可以指定返回数据的条数
SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]
cursor:游标,当次遍历的起始位置pattern:与Keys命令中的patterns相同,支持通配符匹配count:返回数据条数,但只是一个hint(暗示),具体返回条数可多可少。type: Redis 6.0 支持的参数,指定返回Key的类型,类型可选值与 TYPE命令相同:string, list, set, zset, hash and stream。
scan 命令是一个游标式的遍历命令,可以返回符合给定模式的所有键值对。scan 命令可以在不阻塞服务器的情况下返回大量的数据,并且可以通过游标参数分批次进行查询,从而避免了单次查询数据量过大的问题。同时,scan 命令还支持限定返回数量,以及返回指定的字段等高级功能
3.5. redis服务器的的内存是多大?
Redis服务器的内存大小可以根据需求进行配置,可以配置为几十MB到几十GB不等。具体而言,可以在redis.conf配置文件中通过 maxmemory 参数来设置Redis实例的最大内存限制。例如,如果要将Redis实例的最大内存限制设置为1GB,则可以将该参数设置为maxmemory 1GB不设置该参数,则Redis将使用系统的所有可用内存。
3.6. 为什么Redis的操作是原子性的,怎么保证原子性的?
对于Redis而言,命令的原子性指的是:一个操作的不可以再分,操作要么执行,要么不执行。
Redis的操作之所以是原子性的,是因为Redis是单线程的。
Redis本身提供的所有API都是原子操作,Redis中的事务其实是要保证批量操作的原子性。
多个命令在并发中也是原子性的吗?
不一定, 将get和set改成单命令操作,incr 。使用Redis的事务,或者使用Redis+Lua==的方式实现.
3.7. 对redis的持久化了解不?
持久化就是将内存的数据写入到磁盘当中,防止服务突然宕机,造成内存数据的丢失。
类型 | 介绍 | 具体配置 | 优点 | 缺点 |
---|---|---|---|---|
RDB | 默认持久化机制是按照一定的时间将内存中的数据以快照的形式保存到硬盘中 rdb.dump | |||
数据恢复速度快 | 可能丢失少量新数据持久化时存储数据效率低 | |||
AOF | 是将Redis的每一次操作都写入到单独的日志文件中,当重启redis会重新从持久化的的日志中恢复数据 文件名为appendonly.aof | |||
持久化效率高 不会丢失数据 | 恢复数据时效率低aof中记录的所有命令进行重放,效率低 | |||
混合模式(RDB+AOF) | 将RDB和AOF混合一起使用,在使用混合模式时,所有的数据操作也是保存在AOF当中,当进行恢复文件的时候,会将原有的AOF删除,并且将其中的数据全部以快照的形式保存至RDB文件当中 | |||
持久化效率高保证数据的安全性不会丢失数据且恢复的速度快 |
redis的过期策略以及内存淘汰机制有了解过吗
Redis的过期策略主要有两种:惰性删除和定期删除。惰性删除是指在读取键值对时,先判断该键是否过期,如果过期就删除,否则直接返回。定期删除是指Redis定期检查所有键的过期时间,将其中已经过期的键删除。
内存淘汰机制指的是Redis在内存占用达到限制时,如何选择要删除的键。Redis的内存淘汰机制有6种:
- noeviction:达到内存限制时,直接返回错误,不会删除任何键值对。
- allkeys-lru:Redis会遍历所有键值对,选择最近最少使用的键值对进行删除。
- allkeys-random:Redis会随机选择一些键值对进行删除。
- volatile-lru:Redis只会删除设置了过期时间的键值对中最近最少使用的那些。
- volatile-random:Redis只会随机删除设置了过期时间的键值对。
- volatile-ttl:Redis只会删除设置了过期时间的键值对中,剩余时间最短的那些。
3.8. 做过redis的集群吗?你们做集群的时候搭建了几台,都是怎么搭建的?
搭建Redis集群的方式有多种,比如可以使用Redis Sentinel或Redis Cluster。其中Redis Cluster是官方推荐的方式,也是比较常用的一种方式。在搭建Redis Cluster时,需要进行以下步骤:
- 搭建多个Redis实例并配置集群模式。
- 配置每个Redis实例的节点信息,包括节点IP和端口号。
- 启动每个Redis实例,并创建集群。
- 将数据分配到不同的槽位上,可以使用Redis Cluster提供的命令进行数据迁移和槽位分配。
在搭建Redis集群时,还需要考虑节点之间的网络通信、数据同步和故障处理等问题,需要进行适当的配置和调优。
具体操作:
3.8.1. 下载Redis源码,编译安装
$ wget http://download.redis.io/releases/redis-x.x.x.tar.gz
$ tar xzf redis-x.x.x.tar.gz
$ cd redis-x.x.x
$ make
$ sudo make install
3.8.2. 编写配置文件
port 6379
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 15000
appendonly yes
其中,cluster-enabled
参数需要设置为yes
,表示开启集群模式;
cluster-config-file
参数表示集群节点信息的存储文件;
cluster-node-timeout
表示节点超时时间,单位为毫秒。
3.8.3. 启动Redis节点
$ redis-server /path/to/redis.conf
可以使用不同的配置文件启动多个Redis节点,比如:
$ redis-server /path/to/redis-6379.conf
$ redis-server /path/to/redis-6380.conf
$ redis-server /path/to/redis-6381.conf
3.8.4. 创建集群
$ redis-cli --cluster create 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381
然后会提示输入yes确认搭建集群,完成后集群就搭建好了
3.9. 说说Redis哈希槽的概念?
Redis哈希槽是Redis集群的核心概念之一。在Redis集群中,数据会被分布在不同的节点上,而哈希槽则是用来划分数据所在节点的逻辑单位。哈希槽的数量是固定的,Redis默认将其划分为16384个槽位。
当一个新的节点加入Redis集群时,集群会将部分哈希槽从已有节点上迁移至新节点上,直到集群中所有节点的哈希槽数量都比较平均。当有数据需要存储时,Redis会根据数据的键值对应的哈希值,将其映射到一个哈希槽中,并根据哈希槽的分布情况,将数据存储到相应的节点上。
哈希槽的使用使得Redis集群可以更加高效地进行数据存储和访问,同时还能够实现数据的分布式存储和负载均衡。
3.10. 什么是redis的缓存穿透?如何防止穿透?
缓存穿透是指查询一个不存在的数据,由于缓存无法命中,将去查询数据库,但是数据库也无此记录,并且出于容错考虑,我们没有将这次查询的null写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。
解决:
- 对不存在的数据进行数据空值缓存
- 设置白名单;(•可访问的数据id作为偏移值存入bitmaps;•访问时先检查bitmaps)
- 使用布隆过滤器
3.11. 什么是redis的缓存雪崩?如何防止?
大量key集中过期,数据库短时访问量激增
解决:
- 多级缓存架构(Nginx-本地缓存(ehcache/guava) -Redis)
- 锁或队列对并发访问进行序列化
- Key设置过期标志,对即将过期数据进行提前更新,自动续期(类似击穿的解决方案)
- 数据的过期时间使用随机值,分散过期时间
3.12. 什么是redis的缓存击穿?如何防止?
突发热点访问时,热点数据在Redis缓存中不存在或已过期。大量的对热点数据的访问,都将直接访问数据库,造成数据库访问压力短时激,从而增造成故障。
解决:
- 提前预设热门缓存数据
- 实时调整过期时间、自动续期
- 使用锁
- 缓存数据不存在时,把数据库数据放入缓存
4. Kafka
4.1 什么是Kafka?它的特点和使用场景是什么?
答: Apache Kafka是一个分布式的流平台。它的主要特点有:
- 高吞吐量,支持每秒数百万的消息处理。
- 分布式的、可扩展的和高度容错的。
- 可以同时支持发布/订阅和队列消息的处理模式。
- 适用于大规模日志收集、日志聚合和流数据处理等场景。
4.2 Kafka的消息有哪些主要组件?
答:Kafka的消息主要有以下组件:
- Topic:消息的发布和订阅是通过主题来实现的,一个主题可以有多个订阅者。生产者将消息发布到一个主题上,而消费者从它感兴趣的主题上订阅消息。
- Broker:Kafka集群中的每个服务器都被称为broker。
- Partition:每个topic在Kafka集群中都有一个或者多个分区,一个分区就是一个提交日志文件。
- Offset:对于每个主题分区,Kafka在消息传递过程中对它们进行编号,这个编号称为“offset”。
- Producer:负责发布消息到Kafka broker。
- Consumer:用来从Kafka broker读取消息。
4.3 消息的保留策略有哪些?它们分别有什么作用?
答:Kafka提供了三种保留策略:
- 删除策略(Delete Policy):逐出已经过期的消息(比如7天前的消息)。
- 压缩策略(Compaction Policy):只保留最后一个版本的相同键的消息,用于在“键-值”存储中。
- 原始数据策略(Custom Policy):能够应对一个具有特定业务需求的场景。
4.4 Kafka如何保证消费的顺序和可靠性?
答:Kafka可以通过两种方式来保证消费的顺序和可靠性:
- 分区和副本(Partition and Replica):每个分区内的所有消息都是顺序写入的,而且分区具有可扩展性,使Kafka可以分散负载。使用副本,Kafka可以增加容错性和高可用性,以及在节点故障事件时提供无缝恢复。
- 消费者组(Consumer Group):为了保证消费顺序,每个消费者都被分配至某个特定的消费者组内,在同一个组内消费者可以协同工作以实现负载均衡。消费者可以看到消息,但每个消息只会由一个消费者进行处理,这样可以确保消息的顺序和可靠性。
4.5 Kafka的缺点是什么?
答:Kafka的缺点包括:
- Kafka需要额外的组件才可以提供必要的管理和监控。例如,Zookeeper用于Kafka集群和Topic配置,以及Kafka Connect用于数据导入和导出等。
- Kafka Broker在进行故障转移时需要一些准备工作, Kafka集群和服务必须保持可用状态。
- Kafka需要队列和/或分区的写入操作才能开始处理数据。无法逐步处理其中一部分。
4.6 Kafka 实际生产环境中是否会存在消息积压的问题,如果有,该如何处理?
- 增加消费者
4.7 Kafka如何防止消息不丢失?请具体说一下
- 副本存储:Kafka 通过副本机制来保证数据可靠性。副本存储可以提供数据冗余,如果其中一个节点故障,可以通过副本节点的数据来进行恢复。
- 配置消息的最小 ISR:Kafka 支持配置消息的最小 ISR(In-Sync Replicas)值,该值代表了至少有多少个副本节点需要和主节点保持同步。只有当 ISR 副本接收并确认了生产者发送的消息,才会认为该消息已经被成功写入。
- 消费者确认机制:Kafka 支持消费者对消息进行确认。当消费者成功消费一条消息后,需要向 Kafka 服务端发送确认请求,通知 Kafka 服务端哪些消息已经被成功消费。如果 Kafka 服务端收到了消费者的确认请求,就认为该消息已经被成功消费;否则,该消息将会被重新发送。
分区的写入操作才能开始处理数据。无法逐步处理其中一部分。
4.6 Kafka 实际生产环境中是否会存在消息积压的问题,如果有,该如何处理?
- 增加消费者
4.7 Kafka如何防止消息不丢失?请具体说一下
- 副本存储:Kafka 通过副本机制来保证数据可靠性。副本存储可以提供数据冗余,如果其中一个节点故障,可以通过副本节点的数据来进行恢复。
- 配置消息的最小 ISR:Kafka 支持配置消息的最小 ISR(In-Sync Replicas)值,该值代表了至少有多少个副本节点需要和主节点保持同步。只有当 ISR 副本接收并确认了生产者发送的消息,才会认为该消息已经被成功写入。
- 消费者确认机制:Kafka 支持消费者对消息进行确认。当消费者成功消费一条消息后,需要向 Kafka 服务端发送确认请求,通知 Kafka 服务端哪些消息已经被成功消费。如果 Kafka 服务端收到了消费者的确认请求,就认为该消息已经被成功消费;否则,该消息将会被重新发送。
- Kafka 也提供了可靠模式(At-Least-Once,Exactly-Once)来保证数据可靠性。在可靠模式下,一旦消息被成功发送,就不会再丢失或重复发送,保证数据的完整性和正确性