当前位置: 首页 > news >正文

牛客面经1 滴滴社招-002

—二面 1.5h

比较正式的面试官,好像是负责人,三个环节:拷问项目,拷问基础,算法题

0.项目亮点拷打项目

我们项目的亮点呢就是超大数据量的存储依然能完成高性能的读写,我们项目是19年上线的,初期只负责部分线上流量和线下1超体家门店的交易,数据表最大的是一张用户标签数据表,大概只有几十万数据,采用的是mysql单表存储;后来随着需求的拓展,逐渐增加了比如用户模型表、用户包表、分配信息表增几张关联表;后面随着我们业务的扩张,线下几十家门店,数据量达到几百万,为了提高查询性能,通过索引、sql优化和将数据异步存储到es进行查询,提高了查询效率;再后来我们业务规模逐渐扩展到单我们业务线服务超过400家门店,并且支持了3条其他业务线,功能和逻辑逐渐复杂,我们根据业务线进行了分库,每条业务线数据库下根据数据量进行分表,降低每张表的数据量;有些复杂的需要多次join查询的报表逻辑,设计结果宽表对字段进行适当冗余,降低join的次数;对于需要多次join且字段过多不适合宽表存储的操作,我们尝试使用了clickhouse进行存储,发现虽然性能较好,但是不适合频繁的请求,ck的最大操作数太小,并发容易造成请求超时,转而使用es存储结果字段。项目现在的数据量平均是单表几百万,大表基本在大数据表,有过亿数据,C端接口tp99一般在50ms,B端在200-300ms左右,导出报表数据量超过5w的我们采用异步导出,将数据流写入邮件接口通过邮件发送给导出人员。

感觉面试官有可能接着问:

es提高查询性能的原理是什么?

就是分词存储和倒排索引。

存储的时候对数据进行分词,分词以后保存倒排索引,搜索的时候直接通过关键词匹配到倒排索引,然后根据索引中存储的文档地址和分词出现的位置找到对应的数据。

谈谈你对es中索引的理解?

es的索引类似于mysql的一张数据表,主要结构是alias别名,settings配置-配置里配置索引的分片数量和副本数量,mapping映射-索引指向的字段-包括字段名、类型、长度、分词器等,mapping类似于数据库里的表结构

什么是倒排索引?

es在数据存储的时候会将数据分词,比如 i live in Beijing,分词成 i live beijing这三个分词,存储的时候会将这三个分词所在的文档的地址、在文档中出现次数和在文档中出现的位置存储为倒排列表,将倒排列表的集合存储为倒排文件,查询的时候只需要通过关键词匹配到对应的索引,就能找到关键词出现的所有文档及文档中数据的位置。

es支持事务吗?
es不支持事务

你是怎么解决es操作中是事务问题的?
您说的es的事务我理解就是如何保证数据库和es数据的一致性。正常有这种问题的场景都是写数据库后写es,这种场景可以有两种解决方案:一种是加事务,在es操作失败的时候手动抛出异常,让数据库事务回滚,这种方案因为用了事务会一直保持对数据库的连接等待事务内操作执行完成,所以对数据库压力比较大;我们一般用的都是异步同步数据的方法,监听binlog数据库变更日志,异步更新数据到es,这种对数据库性能影响较小,虽然有一定的数据延迟,通常也不会很长,一般都在1s内

1.你在项目中怎么设置的线程池的配置,为什么用这个设计(IO密集, CPU密集 ,cpu核心数 * 2设置的原因是什么)

我们一般配置核心线程数8,最大线程数16,存活时间60s,任务队列长度50(服务器配置是4C8G)

配置如果是IO密集型(频繁数据库/接口服务请求没有什么处理逻辑)通常设置为2*cpu核数+1
如果是CPU密集型(存在大量的内存计算逻辑)设置为cpu核数+1

cpu核心数 * 2设置的原因,是假设IO等待时间和cpu处理时间相同,设置2n倍的线程能够让cpu在IO等待时也可以执行线程,理论上达到100%的使用率,这个2n也只是通用的设置,也可以根据实际的情况设置具体的值,比如IO时间占用80%,就可以设置5n倍的线程数

2.版本发布时恰好有任务正在导出,如何处理这个情况

如果在版本发布时没有提前摘流或没有等待任务跑完就重启服务,就可能会导致任务中断,如果没有数据补偿机制,可能导致数据丢失或者重复处理。

要解决这个问题,首选在上线或者重启服务前,要摘除负载均衡,通过流量监控观察没有新流量进入,通过日志观察没有正在跑数的任务后,才能进行容器的重新启动。

其次跑数任务需要有检查与数据补偿机制,不能等出现问题才进行检查和补偿,跑数异常之后可以根据比如定义一个中间状态标识任务进行到哪一步,后续启动数据补偿任务可以根据状态查询需要补偿的数据以及处理后续处理步骤

3.线程池执行任务的过程,任务线程不够了怎么办,核心线程非核心线程的区别

过程是会先判断线程池中的线程数是否达到核心线程数限制corePooolSize,没达到创建一个核心线程执行任务;如果大于核心线程数但是小于最大线程数maxPoolSize,创建一个非核心线程;如果大于最大线程数会将任务存储进任务队列,等待线程执行完任务,再从队列中获取任务执行;如果任务队列也满了,会根据配置的拒绝策略进行处理。

任务线程不够了怎么办
线程不够的根本原因是任务到达的速度高于线程执行任务的速度,要解决得从两方面处理。一方面要处理输入过快的场景,比如流量激增;一方面要处理线程执行过慢的场景比如cpu飙升、慢sql、请求外部服务超时。

解决方案:
首先要给线程池设置合适的拒绝策略,这是最后的兜底,我们常用的有两种,callerRunsPolicy 如果队列满了,新任务分配给普通线程处理,一般都用这种;还有一种是abortPolicy,就是抛异常,这种需要有数据补偿机制做兜底;

然后可以根据实际情况调整线程池参数,如果是IO密集按照cpu核数*(1+IO等待时间/计算时间)来重新设置线程数。

其他的也可以从sql优化(慢sql处理、批量处理)、代码优化、异步逻辑处理等方面减少任务处理阻塞时间,提高处理速度;

与异步处理逻辑类似的也可以通过消息转发的方式,将消息积压到消息队列里,通过控制消费速度减少并发量;或者对一些不重要的功能进行降级,空闲出更多的线程处理更核心的功能。

核心线程和非核心线程的区别是什么?
核心线程处理完任务空闲时会保留,非核心线程是临时线程,在空闲后如果达到最大存活时间keepAliveTime会被销毁

4.内存放不下导出的数据怎么办
这个问题我也遇到过,我们原先的营销后台也有做大数据量的导出,导出时间过长(十几秒)且经常将内存打到90%以上,如果多个用户同时点击导出,就有oom的风险

我们是用时间换空间,使用异步导出的方法解决的,分页查询数据库,将数据写入文件,每个文件1w条数据,全部写入后将文件转化为数据流,通过邮件服务发送给用户

需要快速导出的场景,我们使用了预加载,以时间单月维度,将导出的数据提前处理好存在文件里,可以直接导出,当前月份的数据才需要从数据库中获取

5.解决过什么感觉比较复杂的问题,分布式锁,线上OOM排查过程

开发订单中心开发过程中遇见过一个问题,在订单支付的时候出现了重复支付的情况

我们订单提单支付的基本流程,是用户点击支付后,会首选弹出京东收银台,收银台底层对接的支付服务(微信、各类型银行卡、白条、E卡),选择支付方式,点击支付,订单状态更新为支付中,再调用支付服务进行支付,支付完成更新台账信息,订单中心接收对账完成消息后更新订单状态为待打印/待出库等后续状态

我们在测试机上发现如果是弱网环境,订单状态更新不及时,订单支付后瞬间还是待支付状态,仍可以再次点击支付,出现重复支付的情况,订单底层支付其实调用了包括订单中台、促销中台、优惠券中台、商品中台的许多服务判断各种促销状态、促销价格,重复点击会触发这些服务的防重机制,反馈在页面上就只一直点击,一直提示提交失败

解决是使用了基于缓存的分布式锁,将订单id+用户pin作为缓存key存在缓存中,超时时间设置为5s,每次请求判断缓存key是否存在,存在则返回文案正在处理中,请勿重复点击;其实如果是自己的数据表还可以根据数据库唯一索引进行幂等性判断,但是其实我们是调用的中台的支付服务,中台有做自己的判断,我们只需要根据返回code码做对应处理就可以。

线上有没有遇到过OOM,你是如何排查定位的,又是怎么解决的

一般如果高并发的创建和处理大量的对象,或者批量处理大对象数据,都有可能导致堆内存不够出现内存溢出。

然后有可能是内存资源耗尽未释放的情况,比如多线程并发处理数据,线程如果执行完任务未及时销毁,后续又在不断的创建线程,可能导致内存资源耗尽导致溢出。

还有就是可能设置的堆内存不够大,不能支持业务频繁的使用,比如系统支持了新的业务线,但是没有扩容或者调大堆内存,就可能导致内存使用率飙升,出现oom的概率大大增加。

我们线上经历过一次因为批量导出大报表导致的oom,因为我们线上会对cpu使用率、堆内存使用率进行监控并设置报警的阈值,超过80%就会告警,当时是晚上九点多收到的电话报警

我们处理逻辑比较简单,先止损再找问题,立马重启服务实例,重启的时候,查询历史的gc日志,查看到有进行gc,查询最近5分钟的cpu使用率监控,发现仍然在飙升一直到顶,于是去查了网关的流量流速,发现流量流入速度平稳,没有出现大量请求的情况,这时候心里大概确认了估计是某个导出或者大对象批量处理逻辑导致的内存飙升,于是去查找了接口调用日志,过滤几个大表对应的导出接口,发现有一个全量导出nps问卷数据的请求,时间和cpu飙升时间吻合,确定是这个原因后立马将这个接口服务下线,重启后内存恢复正常,并且通知业务。

后面我对这些大表导出进行了复盘和改造,首先给这些风险接口设置了降级开关,可以通过配置随时打开关闭业务,出现问题及时止损;然后将超出5w条数据的导出功能全部使用异步邮件导出,分页慢慢将数据查出来写到文件里,最后将文件发送给指定邮箱。

其实还可以调整jvm堆内存大小,但是这些导出其实使用频率不是很高,避免造成内存浪费没有调整

6.拷打JVM基础概念,为啥ThreadLocal 内存泄漏

jvm就是java虚拟机,主要作用就是将java代码编译成可运行的指令,并且可以管理和运行指令,并且管理程序运行时的各种资源。

主要包括堆、元空间,本地方法栈、程序计数器、虚拟机栈几部分。

堆主要用作创建和管理对象,元空间存储类的基本信息以及java编译生成的字节码指令,本地方法栈存储本地方法,程序计数器运行字节码命令,虚拟机栈

(下班下班下班未完待续)

7.redis 基础数据结构,底层实现,适用场景

(未完待续)

8.算法题:递增数据 中间切开,换位置,找中间点,leetcode 变种题,原题是找指定值, 要求用 双指针实现

(未完待续)

http://www.dtcms.com/a/344724.html

相关文章:

  • JAVA国际版多商户运营版商城系统源码多商户社交电商系统源码支持Android+IOS+H5
  • 哈希和字符串哈希
  • STM32 外设驱动模块七:红外反射式光电模块
  • Centos 8 管理防火墙
  • 安装Tailscale
  • Maven初识到应用
  • 【AI应用】向量数据库Milvus详细命令
  • Jenkins + SonarQube 从原理到实战四:Jenkins 与 Gerrit 集成并实现自动任务
  • Linux爆音问题解决方法(隔一会会有奇怪噪音)
  • Go 基础解析
  • 逛越南本地菜市场学英语
  • 异质结3.0时代的降本提效革命:捷造科技设备技术创新与产业拐点分析
  • DSPy框架:从提示工程到声明式编程的革命性转变
  • go 常见面试题
  • 番茄(西红柿)叶片病害检测数据集:12k+图像,10类,yolo标注
  • RAG中稠密向量和稀疏向量
  • 基于抗辐照性能的ASP4644S电源芯片特性分析与多领域应用验证
  • show-overflow-tooltip使用当内容过多不展示...
  • 国密双证书双向认证实践
  • 浅拷贝,深拷贝
  • SkyWalking高效线程上下文管理机制:确保调用链中traceId来自同一个请求
  • 图像指针:高效处理像素数据的核心工具
  • 贪吃蛇--C++实战项目(零基础)
  • 直播间聊天室直播录播消息发送自动对话点赞H5开源
  • Datawhale AI夏令营---coze空间共学
  • RoboTwin--CVPR2025--港大--2025.4.17--开源
  • NLP 场景下的强化学习
  • 数据分析编程第二步: 最简单的数据分析尝试
  • 总线之间的关系,64位32位与DB数据总线CB控制总线与AB地址总线的关系
  • Spring 中 @Import 注解:Bean 注入的灵活利器