字节国际商业化产品实习一面过
青丝几许人非昨
字节国际商业化产品实习一面(过)
自我介绍
然后问我是中科院那个研究所,我说是空间中心,中关村那边的
项目 定时微服务
-
你这两个项目是什么性质的?是实验室做的吗?
还是第一个项目背景是大概是实验室有这样的一个定时场景,然后,嗯,我自己做了一个,一个做自己的一个开发,还没有完全投入使用,但是已经部署到实验室的集群环境中了,需要我讲一下它的具体背景吗? -
你在这里面参与设计的哪一块?
我主要是负责这个存储设计,然后以及这个核心的那个定时任务的一个开发,写相关的接口嗯。比如说这个创建一个任务,以及 MySQL 的这样的表的一个设计,然后创建接口之后,我们要对这个任务进行这个迁移,然后存储到对应的 Redis 里边,然后包括后面的写这个回调接口,比如说业务方需要进行通知,他会写一个空表达式传到我的接口里边,然后我对其生成任务,然后定时定点触发,然后触发一个回调接口,然后这样子。 -
为什么要做一个分布式的一个出发呀?
嗯,分布式是这样的,因为我所我平时科研,然后环境就是在。在这个实验室集群上,然后因为想也是想挑战一下自己,这个因为就是说如,因为就是领导说的,之前就是开组会的时候,大领导是想开发一个这样通用的一个定时服务,就是和下游任务去解耦的,就是我不 care 后面你这个任务具体要干什么,您可以就是理解为一个是一个闹钟,但是我们这个比闹钟的任务稍微多一点,功能多一点,比如说我是可以定时取消的,我也可以生成这个周期性的任务嗯。然后通过这种分布式的这样的一个多机部署一个存储,然后去提高它这个高负载这样的一个性能。 -
负责的那块存储设计,那个mysql的表设计你能讲一下吗?
嗯,好的,面试官就是。嗯,我们现在我先说一下表的设计, MySQL 的表的设计,它主要是我负责的部分是两张表。第一张是一个 timer 表,这个 timer 它有一些,比如说它的业务方的ID,比如说你这个 timer ID 以及它的那个corn表达式,就是一个周期的一个表达式。嗯,传到我的这个主要是跟这个 time 表相关的一个字段,然后另外一张表就是,嗯, time task, time task 就是一个定时器,里边会有很多的这样的任务,比如说我每五秒钟执行一次,它是,就是它和一个 time ID 相关联,但是它可能会生成每五秒钟都会有一个记录存在这个 MySQL 的 time task 这张表里边,这是 MySQL 的这样的一个存储设计。嗯,然后我还引入了这个Redis,嗯,为什么要引入 Redis 呢?其实是这样的,嗯,就是我们这个定时的场景数据,它是具有这个冷热性质的,如果直接把未来,比如说十天半个月之后的任务都生成了,这样会降低我们的这个存储效率,我们是没有必要的,我们所以说我们后台是有个迁移模块,只迁移生成两个小时之内的,我们认为这个两个小时之内的数据是一个热数据,会把它放到这个 Redis 缓存,这是 Redis 使用 Redis 其中之一的一个原因,就是利用它这个冷热特性呃。然后就是说我们还是为了使用 Redis 做这个数据分片,嗯,怎么做分片呢?就是在时间维度上,因为我们要达到一个秒级的这样的一个需求。我们是,嗯,比如说我们把一个小时的任务分成 60 段,每一段是一分钟,落在不同的桶里边做一个二维分片,然后每个分片根据它的这个所在的时间段以及它的桶号会生成一个它的这个分布式锁了一个ID。然后我们多在多机的一个情况下,每一个线程去抢占这把锁,抢占到这把锁就获得了它的执行权呃。
然后所以说 Redis 也支持我们去做这个多级部署,然后在 Redis 里边还有一种数据结构。叫这个 z set,然后有了 z set,它是这个天然有序的,我们把我把它的这个 score 设置成它的时间戳,每个任务的时间戳落在,嗯,这个是这一分钟时间段的时间戳,对它进行排序,它是可以降低这个时间复杂度的一个,然后我们就可以达到这样一,这样的一个秒级的这样一个误差,所以说存储上大概是这样,通过这种二级存储结构去为后面的高性能、高精准触发以及这个高负载去做这样的一个铺垫。 -
嗯,你刚说那个分片是怎么设计的? Redis 的分片怎么设计。
的? Redis 的分片是基于 z 赛的这样的一种存储,我具体一点来说就是在时间维度上,比如说有用户想要每 5 秒钟生成一次,那一 60 秒钟就是 1 分钟里边就会落下 12 个任务,然后都会落到同一个这个分钟段里边。由于考虑到这种高并发的场景,可能同一个分分钟段,同一分钟可能会产生大量的任务,所以说我们在纵向给它分了个桶,对它进行一个取模,然后分到不同的桶里边,然后每一个桶对应的每一分钟作为一个分片,相当于是一个二维分片,然后再存到我们这个 Redis 里,两个小时之内的数据存到 Redis 里,具体是迁,由这个迁移模块去做的。
然后存到 Redis 之后,然后具体的存储是用 z set 它的 k 的话就是桶的 ID 加上时间段的范围,然后它的这个 value 就是它的这个,想一下它的这个value,它的 score 就是时间戳,它的 value 就是它的time, ID 就是它对应的那个嗯。嗯,定时器的ID,嗯,就这样,通过这种 k 也形成了, value 也形成了, score 也形成了,然后就这个 z set 就放进去,然后我们每一秒,然后这样去扫描就可以了。 -
每秒钟扫描一次,是吗?
对。每一个线程一共我们目前是设置了 5 个桶。 -
嗯,每一秒钟扫描这 5 个桶都拿出来,是吧?是。
一个桶里边,您可以理解为一个桶里边的一个一分钟的时间段有一个扫描的线程,只扫一个桶,一个线程扫一个桶。 -
那给做了一个散列,是吧?对,是的,有了解过一些开源的一些定时的设计方案吗?
嗯,这个我其实是前期有一些调研的,比如说,嗯,虽然调研的不多,就是有过了解一点,比如说那个叫什么 XXL job,那个东西可以,也可以做,或者是用消息队列,但是为什么我选择去自己?实现一方面是,嗯,首先就是说如果用那个 XXL job 的话,它和下游的解耦比较重,然后我也想自己去学习一下这种,嗯,想自己去突破一下。然后用消息队列的话,我记得消息队列它有那个延迟的那个消息,延时消息,但是它不适合,就是说做到一个周期性的一个触发。还有我还调研过像 Linux 它本来也有 Lux 系统,它本来也有这种给它一个课文表达式,它也可以周期,它也可以定时去说话,但是它这适合单机,它这个也是不支持周期的,也不支持。包括消息队列也是,也不支持我们去定时的取消这个任务。我们我做的这个就是可以定时的取消了之后任务就不会被执行到点了,也不会叫相当于是闹钟,我明天 9 点闹钟我要关掉,我可以在 9 点之前关掉, 9 点之后他就不会再闹了。 -
你这个 timer 能编辑吗?定义好了之后。
嗯,timer,嗯,是创建之后是无法修改,但是目前就是说可以给他,就是取消给他 concern 掉,嗯,可以, concern 之后再创建一个新的,也是 OK 的。 -
有碰到过什么问题吗?在这个实际的应用当中。
嗯,其实碰到问题就是在于那个,嗯,主要是这个高精准之前就是,嗯,不知道怎么去想。还有主要是高负载这一块,就是把它误差缩短为一秒,所谓的这个高精准就是这个误差嘛。这个误差是什么呢?就是我们预定的这个用户预定的这个触发时间,以及我们实际触发时间之间的这样的一个误差。然后我做了一个实验,大概就是我们当我们的任务并发,就是一次到这个 1, 000 到 2, 000 的话,是基本上能到这个 1 秒以内的误差的。然后还有一个问题就是我测试,我压测了一下这个 MySQL 的这个承受能力,也就是这个创建的这个接口,以及取消的这样的一个接口,尤其是创建,尤其他刚开始他那个 QPS 是比较低的,好像是就五六百的样子,然后后面,嗯,在网上找了一下资料,然后嗯是可以优化它那个数据库的连接池的。然后在那个 XL 里边进行了数据库连接池的一个配置,然后它的 QS 就上来了。 -
我看还用了分布锁,用分布式锁干嘛?
分布式锁是这样的,就是说因为我们要支持多级部署,你们不可能说就是一个机器就用一个机器,那其他的机器干什么呢?就是说我们 Redis 是统一存储嘛?我们每个机器有那个这个触发模块,有这个有那个调度模块,这个主要是和调度模块有关,每个机器它调度模块会有一些限。
城每个线程去抢这个 Redis 的一个分片,就我刚给您讲到的一个分片会对应一个分布式锁,抢到这个分布式锁的就意味着这个线程拿到了这个桶的执行权,这个桶在这个时段的这样的一个分片的执行权,然后由他去执行,他在执行的时候里边就会由这个线程去扫描这一分钟这个桶里边的任务,然后扫描到了之后他会再去创建一个,用到了再会创建新的那个执行线程,去进行后面的一个触发的一个流程。 -
分布式锁是具体是怎么实现?
嗯,分布式锁我是用那个,直接用的就是那个 set NX。嗯,因为分布式锁它有那个要涉及到过期时间,这也是,其实也是用到这个 Redis 去,它支持一个多机部署,然后多机并发的一个关键,我觉得就是设置它的过期时间。嗯,说到过期时间,你这个所拿到这个一个线程,拿到这个分片的执行权之后,嗯,就是说,比如说我执行完了,他,如果嗯在同一分钟又被其他的线程拿到了这把锁的话,那这样的话这个时间段是有可能被重复执行的。考虑到这个问题,我就把一个分片的时间,嗯,把它设置成了大于 1 分钟,就是大于 60 秒,我就设置了成了一个 70 秒这样子,嗯,然后去这样去做一个分布式锁,然后到期之后它会自动的。嗯,过期也会配合自己Redis,它里边有自动的这个缓存删除策略吗 -
有了解过set nx 的底层原理吗?
set NX 这个没有具体了解过它的底层原理。-
检查键是否存在:执行
set nx
时,Redis 首先检查键是否已存在。如果键存在,命令不执行任何操作并返回nil
;如果键不存在,则继续执行。 -
原子性:
set nx
是原子操作,确保在高并发场景下,只有一个客户端能成功设置键值对,避免竞态条件。set nx 是原子操作这个是怎么确保的?
SET nx
的原子性由 Redis 的单线程模型和底层实现机制共同保证:- 单线程确保命令按顺序执行,不会被其他命令打断。
- 键空间检查与设置操作是连续的,类似于 CAS 操作。
set nx和setnx有什么区别?
前者灵活性更好,set nx更适合加分布式锁,因为可以设置时间ex
-
-
嗯, z set 的底层是什么数据结构?
嗯, z set 的话,嗯, z set 的底层是这个,嗯,如果说数据好像比较小的话,它是那个 zip list。然后数量比较大的话,它是那个 hashtable 和那个跳表结合。 -
里边还有什么数据结构吗?常用。
的 Redis 里边有这个像最多的那个Redis,其实它每个对象其实都是一个,他的那个 k 都是一个字符串。所以说最经典的就是那个字符串,它底层是基于这个SDS,它的编码方式大概有三种。这个需要我说一下吗?还是说直接过一下?留言到过,嗯,行,然后这是第一种就是那个嗯字符串,然后第二种就是这个list,它是一种双端队列,就是支持两端进出的这样的一个k,我是这样理解的,这样的一个list,然后呢?然后还有这个哈希table,嗯,然后还有。set,然后就是最后就是我用到的这个 z set。 -
嗯,你刚讲到有 time 还有 time task,是吧?嗯,对。然后 time task 是一个拆分。
是吧?对, time task 是基于存,它是存储在这个 MySQL 里边的一个兜底的一个存储,就是对应每一个任务,比如说 5 秒之内,每一秒都有一个任务,那它就会存 5 个,都会存到 MySQL 的 time task 里边。
sql
找出导出 task 最多的 timer 就基于你那个表的设计
这里有点紧张
1 | select |
算法
给定两个字符串,返回他们俩的最长公共字串,然后子串必须是连续的,除了求解长度还有返回公共部分
这里刚开始的写的时候还是有点紧张,虽然写了很多遍了,好在最后写出来了
时间复杂度问了一下(n^2)
八股文
-
你讲一下 Java 里面的一些线程池的一些设计吧
这里讲了线程池的理解,但是有个地方讲错了,没有说到临时线程
-
Java 里面的线程池的实现有了解过吗?
没有手动实现过
-
java里的锁了解过吗?
说了一下synchronized、lock、cas、volatile等
-
Java 里面怎么去创建一个线程呢?
四种方法吟唱
-
Java 里面有哪些常用的集合类?
-
concurrenthashmap和hashmap的主要区别是什么?
-
那能讲一下 MySQL 里面的事务的几个特性吗?
聊天环节
- okay,我看可以实习 6 个月以上,对吧嗯?对。
因为我。的平时都可以实习周一到周五。
我可以周一到。我是这样的,因为我们学校就我现在我们所就是我的课题已经完成了,然后已经征得了导师和实验室领导的同意,他们同意我出去。所以说,嗯嗯,我可以投入到更多,也可以就是说,嗯,实习半年以上都是 OK 的。 - 周一到周五都可以,是吧啊?都可以。是的,行。唉,有用到一些大数据的技术栈吗平时?
嗯,大数据的没有用到过,但是我的课题是处理过一些,用 Python 去处理过一些数据,几十个g,这样也不算太大。 - 行,你有什么想问的吗?
嗯,我想问一下您对我今天的嗯评价如何?我有什么需要加强的地方?我好下面去学习,继续加强。
嗯,就是有一些那个基础掌握的还可以,就是那个有一些细节可能还需要再看一下,比如说sql的一些语法,看起来,嗯,不是特别的熟悉,唉,好,其他都还好。 - 嗯嗯,那我还想请问就是您这个就是,嗯部门主要是做什么的?是不是技术栈主要是偏向于哪个方面?
这边主要是做是商业化,下面做一些合规相关的,主要就是做一些那个数据相关的一些分类,数据分类,然后数据删除,然后数据打标这些工作,然后具体这个职位可能会涉及到一些 Java 后端系统的一些开发,以及一些那个大数据相关的一些那个 circle 的一些数据处理。对,然后技术栈基本就是,嗯,Java、MySQL、Redis,然后 spring 这些后端的架构,以及像哈沃斯考,像写一些那个嗯。嗯,福林克这种思考任务,对,大概的技术量就是这样。 - 好,那会有go方面的这个吗?
我听我们这个职位暂时没有。