1.介绍一下自己
面试官您好!我叫***,目前是就读于****计算机科学与技术专业的一名学生。我平时在学校也自学了编程相关的知识,比如Java基础、Springboot、SpringCloud,关系型数据库Mysql,非关系型数据库Redis,还有一些服务治理组件像Nacos、Sentinel、Seata、RabbitMQ等,并积极地
参加学校的社团和竞赛等来提升我的能力,我平时也经常会浏览一些技术相关的博客和文章,并且我也会将自己在学习过程中所遇到的问题以写博客的方式记录下来以便巩固知识。以上就是我目前的一个情况。
2.介绍一下你的这个晓时宠物医院平台这个项目吧(开发的原因)
晓时宠物医院平台,这是我第一个由自己独立搭建的Spring boot项目,当时也正好有一个契机,就是家里的猫生病了然后想着现在网上的宠物医院挂个号嘛,然后发现那个系统使用起来特别不方便,首先就是页面的UI做的很简陋,还有就是我在访问的时候很难去清楚地了解到医院当前提供了什么服务和医生的排班情况。导致我在上面花费了很长时间。之后我就在想我能不能自己试着做一个宠物医院平台,然后就做了这么一个项目。
3.具体实现了什么功能?
晓时宠物医院是基于Spring boot的B/S架构单体架构项目。用户操作全流程数字化,覆盖实际赴诊前的一系列操作,优化了以往的在线就诊预约平台的手续繁杂、理解困难等问题,此项目采用较前沿的技术栈和架构思想,并可以在后续扩展应用于更多种类的相关平台。此系统主要用于实现用户在网上在线选择服务并预约服务的功能,方便用户的操作,节约用户的时间。用户可以在登录后可以在用户中心对宠物信息进行完善,并可以查看已有的预约信息和历史订单等数据。在就诊界面上用户可以针对宠物的情况自由的选择一个套餐服务并可以选择任意的医师进行预约,系统会为用户展示医师的排班时间表。提交订单后系统会为用户生成一个订单为其安排就医时间,用户在规定时间可以到线下携宠物就诊。同样的用户可以在就诊前取消订单。系统同样提供了用户对任何服务的评论和点赞功能,以便使用者可以了解到真实的信息。在管理端系统则提供了管理用户信息、宠物信息、预约信息、库存信息等数据并加以管理。
4.那你的一些访问频率比较高的数据是怎么处理的?
我会提前将一些访问频率大的数据写入redis进行保存,以减缓数据库的访问压力。然后我具体对redis的操作是这样的:在启动服务之前,我首先将一些热点数据提前进行写入redis缓存,进行缓存预热,然后在缓存容量达到设定的阈值后采取LFU内存淘汰策略,来保证缓存中永远是热点数据。对于排行榜的实现,我采用了SortedSet来实现。
5.你说一下你的这个乐享云购项目吧
这个项目是我在github上拉取下来进行模仿开发的,因为现在线上购物比较流行,比如京东、淘宝都是做的非常大的平台,我对它们的业务流程、细节都很感兴趣,正好github上面有开源的这么一个项目,我就学习了一下项目的思想,然后仿照着这个项目进行开发。乐享云购这个项目也是实现的在线购物的功能,包括用户管理、商品管理等等功能
6.你的项目中分了几个微服务?分别是什么?
我的项目主要分为了“用户服务”、“商品服务”、“网关服务”、“远程调用服务”、“工具服务”、“支付服务”、“交易服务”等。
7.你是怎么实现数据库的分库分表的?
首先,数据库分库分表主要分为水平分库分表和垂直分库分表,水平分库分表是根据数据的量的大小进行分割,比如1-10000号存在一张表、10001-20000存在另一张表;在我的项目中,我主要采用了垂直分库分表,也就是根据一些表的字段进行拆分,对于一张表中关系相对疏远的字段进行拆分,这样也可以减少单表的数据量大小,以减少表查询时的压力。
8.那你有没有了解过分库分表的工具?
虽然我并没有实际的深入的去使用过,但是我之前在牛客上面刷到过有些人分享他们会在分库分表时利用一些工具进行协助,可以大大的提升效率。然后我了解的大致有以下这几种:
1.ShardingSphere
2.MyCAT
3.DBLE
9.假设说服务的访问量很大,超过了服务的承受范围,你是怎么处理这个问题的?
我是采用了Sentinel组件来帮助我进行服务的治理,比如可以在Sentinel中设置服务的最大QPS,当访问量超过设定值的时候就采取熔断,以及设置失败率阈值等等操作。因为大多数服务的请求结构都是树形结构,即最终有某几个服务需要承受来自许多的服务的请求,这种情况下根服务就很容易挂掉,一旦它挂掉,就会导致服务大面积的不可用,也就是服务雪崩,采用Sentinel可以及时的对服务进行熔断以及采取降级策略。
10.既然你说到了降级,那么你说说你在项目中具体是怎么实现这个功能的
因为我编写了服务远程调用的服务模块,在openfeign中可以手动定义fallback接口,fallbackFactory类实现fallbackFactory<>接口,在其中创建create方法并返回一个重写实现的远程调用的接口的匿名内部类,在其中定义降级逻辑。
11.你是怎么进行事务控制的
我是通过Seata进行的事务控制,因为这是一个微服务架构的项目,而常规的事务控制方案只能管理单节点的事务,所以我采用了Seata的分布式事务的解决方案,它将事务分成多个小的事务,由全局事务协调器统一进行管理。
12.那么在Seata中有哪些模式,你的项目使用了哪个模式?
Seata中有 AT 模式 、TCC 模式 、SAGA 模式 和 XA 模式 。AT模式采用两阶段提交的方式,代码侵入性低,采用最终一致性的思想;TCC的原理是将事务的执行分为三个阶段,try、confirm、cancel根据执行的结果判断是否回滚,是强一致性的解决方案;SAGA是基于长事务的方案,将事务分为许多小事务,由统筹器管理;XA是强一致性方案,提前锁定事务和资源,最终统一处理。我的项目中采用的是默认的AT模式,因为它对项目的侵入性较低,且适合大部分业务场景。
13.在你的项目中有没有一些调用链比较长的业务,你是怎么处理他们可能出现的问题的。
在我的项目中执行链较长的业务就比如支付模块,他是有这样的一个逻辑,用户添加完商品先提交订单,商品服务执行库存预扣减,然后后端服务器接收到请求就由交易模块来执行创建订单的操作,然后等待用户支付再去执行一个更改订单状态的操作,最后在将购物车的商品清空。
但是因为这些操作是通过远程调用实现的,所以就有可能发生一些错误导致订单状态没能及时改变,比如用户交易后交易模块发生错误没有成功的向订单模块发送支付成功的消息,这样就会导致一些大的问题,这是最危险的。所以我就在上面加了一个兜底策略,就是说在创建订单的时候就会发送一个延迟消息,时间设为15分钟,因为支付所允许的时间就是15分钟嘛。在15分钟后就会去主动查询订单的一个状态,如果说订单显示已支付就表示正常执行了,如果显示未支付或者其他状态,就去检查流水,如果发现钱已经打到账上了,那么就说明其中发生了一些问题,就把订单状态改为已支付,如果没有查到账目,就表示没付钱嘛,就把订单状态改为已取消。
14.我看你有做过数据库优化,你有哪些方法区优化数据库的访问压力?
数据库在我们的业务中是非常重要的,所以保证数据库能够正常运行是必不可少的。我一般会通过一下的手段进行数据库的优化。
1.首先我会考虑对数据库的访问加一层缓存,比如本地缓存、redis缓存等,他们都可以阻挡大部分的请求打到数据库。
2.缓解数据库查询时的压力同样可以对数据库本身做一些操作,比如在热点字段上建立索引,不易更改的数据且访问量大的数据建立视图;或者我们可以将读并发量高和写并发量高的数据分开存储,使用不同的存储引擎;同时在数据量极高的情况下我们可以通过分库分表的操作减轻查询负担,减小死锁和阻塞的产生概率;再进一步的话,对于高并发量的请求,我们可以对数据库做集群处理,搭建主从库,实现读写分离,避免单节点数据库崩溃导致的服务不可用。
3.一样的道理,我们同样可以多设置几层本地缓存和对缓存做集群,而redis是天生支持这一点的,redis可以做集群部署,搭建主从节点,实现读写分离,这样可以大大的减少数据库的访问压力。
以上就是我对于数据库优化的一些想法
15.既然你提到了存储引擎,那你说一说有哪些存储引擎,他们的区别是什么?
最常用的存储引擎是MyISAM和InnoDB。
他们主要有以下区别:
存储空间上:MyISAM可被压缩,存储空间较小;InnoDB需要建立缓冲区缓存数据和索引,所需空间较大
文件格式:MyISAM的索引和数据分开存储(.MYD和.MYI);InnoDB的数据和缓存集中存储
存储顺序:MyISAM按插入顺序存储;InnoDB按主键大小排序
外键:MyISAM不支持外键;InnoDB支持外键
事务:MyISAM不支持事务;InnoDB支持事务
锁粒度:MyISAM支持表级锁;InnoDB支持表级锁、行级锁
执行效率:MyISAM新增更优;InnoDB删除、修改、查询更优
索引:MyISAM是堆表,不支持哈希索引支持全文索引;InnoDB是索引组织表,支持哈希索引不支持全文索引
存储结构:MyISAM 表信息被放在三个文件(表结构、索引、数据);InnoDB表信息被放在一个文件当中
16.你说一说有哪几种索引
mysql中索引主要有1.主键索引,主键索引每张表只能有一个,不能重复,效率最高;2.唯一索引,表示表中该字段是唯一不能重复的,一张表可以有多个;3.普通索引,可以重复,一张表可以存在多个;4.全文索引,搜索引擎的常用技术。
17.mysql中undolog、redolog、binlog的作用,你结合ACID特性说一下
首先ACID表示数据库事务的特性,分别是“原子性”、“一致性”、“隔离性”、“持久性”。
undolog是一个回滚日志,它的的作用是对数据的版本做控制的,在mysql的MVCC中作用很大,所以undolog主要体现了ACID的原子性和一致性。
redolog主要用于数据库崩溃的数据恢复,将数据写入磁盘,所以redolog主要体现了持久性。
binlog主要用于数据库主从复制和数据恢复,其是一个二进制文件,记录了执行的操作信息,同样也在MVCC中实现了事务的隔离,所以其体现了一致性和隔离性。
18.说一下JVM有哪几部分,分别有什么作用
JVM主要分为四大区域。
类加载器:用于将.java文件翻译为.Class文件并读取为JVM内部的存储结构
运行时数据区:其主要包括虚拟机栈、本地方法栈、程序计数器、堆内存区、方法区。用于保存程序运行时的类、方法、变量等信息。
执行引擎: 包括即时编译器、解释器、垃圾回收器,作用是将字节码文件翻译为系统指令交由CPU执行。
本地库接口:其中存放的是由c语言编写的本地方法,在运行时由本地方法库接口提供支持
19.线程池有哪几种?
线程池主要有以下几种:
1.newCachedThreadPool():创建一个可缓存线程池,若线程池长度超过需要则回收,不够则创建。
2.newFixedThreadPool(): 创建一个定长线程池,控制最大并发数,超过数量的请求放在队列中等待。
3.newScheduledThreadPool():创建一个定长线程池,周期性的执行任务。
4.newSingleThreadPool(): 同时只让一个线程执行任务,可指定优先级(FIFO、LIFO、优先级)
5.ThreadPoolExecutor(): 最常用的线程池,可以自定义各种参数
20.讲一下线程池的参数有哪些
线程池有7大参数:
1.核心线程数 2.最大线程数 3.存活时间 4.存活时间单位 5.线程工厂 6.阻塞队列 7.拒绝策略
21.你是怎么保证并发场景下数据的一致性的
1.在并发场景下,可能会出现多个线程对同一个共享变量进行操作的一个场景,那么我们首先就是要对这个变量加上volitile的关键字,他就可以保证一旦被修改就会加载到内存中使其他线程可见,但是它并不能保证先策划给你的安全,所以我们就要考虑加锁,这个时候就要注意加锁的一个顺序,要避免死锁。然后使用一些并发安全的容器,比如concorrentHashMap、copyonwriteArraylist等。
然后要保证数据的一致性我们可以考虑给数据库表加上一个互斥锁或者是乐观锁,又或者是考虑通过依靠数据库的隔离级别来保证数据的一致性。
22.mysql有哪几种隔离级别
mysql中主要有以下四种事务隔离级别:
1.读未提交
这是效率最高但是安全性最低的隔离级别。
2.读已提交
用户只能查询已经成功提交的数据,可以解决脏读的问题
3.可重复读
Mysql默认的隔离级别。用户在同一个事务中的多次读取数据的结果是一致的。可以解 决脏读和不可重复读 的问题。
4.序列化
序列化是mysql最高的事务隔离级别,可以解决脏读、不可重复读、幻读。
22.说一下你对分布式锁的理解
在微服务架构的项目中,由于我们的项目拆分为多个不同的模块,相互独立。在执行业务的过程中,我们在保证共享变量的操作互斥的过程中,通常会利用锁机制来实现,但是常规的锁只能锁住当前的节点,而在分布式场景中,通常单个服务会启动多个实例,这样就没法保证资源和操作的互斥。分布式锁可以保证多个实例在同一时间段内,只有一个客户端可以获取锁资源。通常会通过redis或zookeeper实现,我的项目中是通过redis的setnx命令实现的分布式锁。