场景题整合
如何设计网约车系统?(哈啰)page 63
架构:乘客端用HTTP请求通过负载均衡和网关访问服务,司机端采用TCP长连接实现实时位置更新和消息推送。
核心组件: TCP长连接管理集群:处理司机位置更新和消息推送。通过Zookeeper维护TCP服务器状态。redis记录司机ID与服务器映射关系。
订单管理系统:处理订单状态变化。
派单引擎:实现智能派单。三秒一批处理订单,优化整体等待时间,而非单个订单最优。
地理位置服务:基于redis GeoHash实现距离计算。按照城市或区域分片存储位置数据,降低单个跳表压力。
消息队列:处理订单分发。
500个人去抢一瓶茅台,两个服务器,一台服务器负载300,数据库负载10,设计一个秒杀系统。(得物)page 63
架构设计。1.前端层。用CDN展示秒杀活动。2.负载均衡。用负载均衡器将请求分发到两台业务服务器。3.业务服务器。秒杀逻辑靠近内存操作。4.缓存层。用redis存储库存,用lua实现库存扣减。5.消息队列。请求在redis库存扣减成功后,将订单信息发送到消息队列中。6.数据库。持久化数据,异步从MQ批量写入。
流程。1.用户访问,通过CDN访问秒杀页面,点击抢购,发送请求到负载均衡器。2.请求分发。3.库存检查与扣减。服务器先从redis检查库存,用lua原子扣减,库存充足则扣减成功。4.消息队列。扣减成功后,将订单请求放入MQ,并向用户返回抢购成功。5.异步写库。后台从MQ获取订单请求,结合数据库事务进行订单持久化,更新库存状态。 <!– 系统架构:
Nginx负载均衡,请求分发到两台服务器。 风控系统,增加验证码或滑块验证,根据IP,用户ID进行限流。 应用服务器,处理业务逻辑。 缓存redis,用于库存扣减。 消息队列rocketMQ,异步处理订单,削峰填谷。 数据库,最终一致性,持久化订单数据。
解决方案:
库存预热到redis。将库存预热到redis。用DECR命令保证扣减原子性。当库存为0时,设置标记,后续请求直接返回。
用消息队列异步下单。当用户成功扣减redis库存后,将下单请求写入消息队列。后端消费队列,创建订单落库。
防止超卖。用redis分布式锁,保证一致性。在数据库操作时,用版本号防止并发更新超卖。 –>
线程池如何将20个线程的结果一起返回?(线程池如何将多个线程结果一起返回)(满帮)page 1
用ExecutorService和Future:ExecutorService可以提交任务返回Future对象。
用CompletionService。
用CountDownlatch:等所有线程完成再统一获取结果。
ExecutorService executorService = Executors.newFixedThreadPool(20);
List<Future<String>> futures = new ArrayList<>();
for (int i = 0; i < 20; i++) {
Future<String> future = executorService.submit(() -> {
// 模拟任务执行
return "Task result";
});
futures.add(future);
}
List<String> results = new ArrayList<>();
for (Future<String> future : futures) {
results.add(future.get()); // 获取每个线程的结果
}
executorService.shutdown();
ExecutorService executorService = Executors.newFixedThreadPool(20);
CountDownLatch latch = new CountDownLatch(20);
List<String> results = new ArrayList<>();
for (int i = 0; i < 20; i++) {
executorService.submit(() -> {
try {
// 模拟任务执行
results.add("Task result");
} finally {
latch.countDown(); // 任务完成后减少计数
}
});
}
latch.await(); // 等待所有线程完成
executorService.shutdown();
实现一个栈的自定义函数increase,从栈底每个元素增加一个给定值,怎么优化?(设计一个栈,能对栈底连续若干个元素增加一个固定值)(百度)page 1
用辅助数组记录增量,不立即更新栈的每个元素。时间复杂度O 1,空间复杂度O n。
电商系统的库存数据表怎么优化?(百度)page 63
参考。 库存表包含信息:商品sku,仓区,实时库存,锁定库存,待退货库存,活动库存。
仓区用来区分不同地区仓库同一个sku。锁定库存是用户已提交订单到实际扣库存或订单失效这段时间里锁定的库存。还可以设置商品库存状态,如虚拟库存,不需要设定库存,可以任意由用户购买,则用户每次购买时不用查询库存。
参考2。
生产者消费者模式:生产者创建订单,队列存放订单,而从队列中消费订单则是数据库扣除库存的操作,其中存放订单的队列可以缓冲高并发给数据库带来的压力。
代码if else比较多情况下,如何优化代码?(腾讯,百度)page 63
策略模式。可以将每个条件分支封装成一个策略类,通过上下文选择具体的策略。
表驱动法。通过查表代替复杂的if else逻辑。将条件和对应的操作封装在一个表中。
多态,将每个条件封装成一个子类,通过父类引用调用子类的方法。
下游服务接口很多参数,逻辑很多分支,怎么设计可以使得统一收口并且可以配置化?(百度)
用策略模式,以及API网关做到配置化。
火车票怎么更新库存?(得物,小红书)page 62
火车票库存可以表示为一个二维矩阵,其中行表示站点,列表示车次。每个元素表示站站的车票数量。
库存更新。当用户购买车票时, 系统要将从起始站到终点站所有站站库存减去相应车票数量 ,同时查询的人很多,虽然具体座位票只能卖给一个人,但余票可能很多,而且不能预测哪些查询者会购票,适合乐观锁保证数据一致。
高并发处理。分布式锁,用redis或zookeeper实现,保证同一时间只有一个请求可以更新库存。
假设你是 360 安全管家的开发人员,现需要开发一项功能,为用户显示本次开机耗时多少秒,超过了多少用户。耗时多少秒技术问题已经解决,请问如何实现“超过了多少用户”的需求?用户规模上亿。(在超过一亿用户的开机耗时数据中,如何快速统计出比当前用户开机耗时更慢(即耗时更长)的用户数量。)(京东)page 62
数据收集与存储。由于用户规模庞大,可以使用分布式存储系统如Hadoop或HBase存储数据。
数据分析。使用大数据处理框架如Spark或Flink进行实时或批量处理,计算出开机耗时分布情况。
百分位数计算。通过计算百分位数如90百分位数,确定超过多少用户的时间阈值。
结果展示。将计算结果存储在数据库中,并通过api或前端界面展示给用户。
kafka并不支持延迟消息,如果让你来实现你怎么做?(快手)page 62
定时任务,定期检查外部存储中延迟消息,达到延迟时间后发送到kafka,比如quartz或者spring的定时任务功能或者xxl-job。
分区设置不同的延迟时间,比如一个topic有p0是1分钟,p1是3分钟,p2是10分钟,根据延迟时间选择分区。
使用外部存储,用redis或MySQL管理延迟消息,记录消息内容和预期的发送时间。难点是并发量,可以用分区表,按月周天小时来分。也可以用Redis的z set数据结构,将消息的发送时间作为score,这样可以方便地按时间顺序取出消息。
怎么设计购物车系统?(腾讯)page 61
系统架构 。采用分布式架构,保证系统高可用和可拓展性。购物车服务可以独立部署,通过 微服务 方式与其他服务如商品服务、用户服务进行交互。
数据存储 。购物车数据需要快速读写,我选择 redis 作为主要的数据存储。redis的内存存储特性可以确保购物车操作的低延迟。同时,为了数据持久化,可以将购物车数据 定期同步 到mysql数据库中。
功能模块 。购物车系统主要功能包括添加商品,删除商品,修改数据量,结算。
并发控制 。我会使用乐观锁或悲观锁确保在多用户同时操作购物车时数据的一致性。比如在修改同一商品数量时,可以使用CAS操作避免数据冲突。
用户体验 。购物车需要实时更新。当有用户添加商品到购物车时,商品数量应立即更新,可以通过websocket或长轮询实现。
购物车至少要有skuid,数量,加购时间,勾选这些属性。在设计购物车存储时,要考虑多端同步,以及要实现用户未登录的暂存购物车。
1000个人中有一位新冠患者,如何用最少的试管和检测次数,找到这个患者?(十个小白鼠,1000瓶水,怎么试出来哪瓶水有毒?)(京东,shopee)page 61
因为2的9次方小于1000,1000小于2的10次方,我们需要10位二进制编号人.用10个试管,试管1收集编号中最低位为1的所有人的样本,试管2收集编号中第二位为1的所有人的样本,试管10收集编号中第10位为1的所有人的样本.通过将10个试管的结果从最高位到最低位组合成一个二进制数,可以定位到患者编号.
给你1000个小球,让你把他们放到10个箱子里面,然后再给你一个1-1000的数字,要求能从这些箱子中找到若干的箱子他们的小球数量等于这个数字。(满帮)
和上题不同,2^10=1024,1到512加起来是1024,但是总共只有1000个球,512应该改成489。
对于1到511,仅用前9个箱子组合。512到1000,用第10个箱子加上前面的箱子的组合,比如表示600,用489+111。
1亿数据只有1G内存,怎么去重?或者已知某个大文件内包含大量电话号码,每个号码数字为8位,怎么统计不同号码的个数?(在仅有1GB内存的条件下,如何从一个包含1亿条数据的大文件中去除重复数据,或者统计该文件中8位电话号码的不同号码数量?)page 60
bitmap 对每条数据用1bit标记是否存在即可,1亿bit约等于12MB,bitmap适合值域较小,数据密集场景。 布隆过滤器 当值域较大时,使用布隆过滤器,对于数据a,用k个哈希函数,计算出k个哈希值,在bitmap中将这k个位置都标记为1,表示这个数据存在,适合不严格去重场景,当判断存在时,数据可能不在集合中。
设计一个短连接系统(字节)page 74
需求分析:功能需求:长链接转化为短链接。用户访问短链接时,系统能重定向到原始长链接。用户可以自定义短链接内容,只要没有被占用。后台数据管理。
非功能需求:每秒数万级别并发。高性能,平均响应时间小于10毫秒。短链接不可预测。
架构设计:短链接生成策略:预生成短链接池,存储在hdfs,需要时,从池中取。
存储方案:长短链接映射关系:用 HBase 分布式数据库, 短链接为键,长链接为值 。
缓存层:用 redis 存热点数据。
高并发:用Nginx反向代理服务器,请求分发。
详细设计:短链接编码:用URL安全的base64字符集,+替换为-,/替换为_。6个字符的短链接,64^6,600亿种组合满足需求。
预生成短链接池:用高性能随机算法生成指定数量短链接存储在hdfs。
用户自定义短链接:限制自定义短链接最小长度为7。
重定向响应处理:用302重定向,响应包含location头部,指向原始的长链接地址。
假如有一个 1G 大小的文件,文件里每一行是一个词,每个词的大小不超过 16 bytes,要求返回出现频率最高的 100 个词。内存限制是 10M。(在内存只有 10MB 的情况下,如何从一个 1GB 的文本文件中快速找出出现频率最高的 100 个单词?)page 74
分块处理,要将文件分成多个小块处理。假设文件大小为1000MB,可以分成100个10MB的小块。 局部统计,对每个小块进行词频统计,并记录每个小块的topk词。对于每个块,我们用 hashmap 统计每个词出现的频率。统计完成后,我们用 最小堆 维护当前块的topk词,最小堆大小为100,确保我们只保留频率最高的100个词。 为什么用最小堆?最小堆堆顶元素是最小元素,每个节点的值小于等于子节点的值。如果堆的大小超过了k,那么移除堆顶元素可以保证堆中仍然是当前最大的k个元素。 全局合并,在所有小块处理完成后,得到了100个top100的词列表,接下来我们可以再用 最小堆 合并这些列表,得到全局top100词。
给定ab两个文件,各存放50亿个url,每个url占64字节,找出ab两个文件共同的url,内存限制是4G字节。(在内存仅有4GB的条件下,如何从两个各包含50亿条、每条64字节的URL文件中找出重复出现的URL(即两个文件的交集)?)(两个500G的文件存ip地址,给30G内存,求两个文件的交集)(两个文件各有一亿行字符串,写一个算法找两个文件中相同的字符串)(大文件diff)(度小满,百度,shopee)page 88
分治。将两个文件分别分成多个小文件,每个url64字节,50亿个,大概是300G字节,把每个小文件大小控制在4G字节以内,可以设置80个小文件。 哈希分桶。我们用 哈希函数 将url映射到不同的桶中。具体来说,我们对每个url进行哈希,然后将哈希值对小文件数量取模,把url 重新分配 到不同的小文件中。 逐个比较。对每个小文件,将其中一个文件内容加载到内存中,然后逐个检查另一个文件中url是否存在于内存中,如果存在,则说明这个url是两个文件的共同url。 结果合并。将所有小文件找到的共同url合并起来,得到最终结果。
给定100亿个无符号乱序整数序列,如何求出100亿个数的中位数,内存只有512M字节。(腾讯)page 60
采用分治思想,对大文件进行按位划分,步骤如下。
初步分区,按最高位划分。1.每个数用32位二进制表示,先根据最高位划分。2.将数据分成两个文件,file_0存放高位是0的数字,file_1存放高位是1的数字。3.假设划分后file_0有60亿个,file_1有40亿个。4.中位数必定在file_0中,且它在file_0中的位置是第10亿(这里假设整数是有负有正的)。
进一步划分。1.对于file_0继续利用下一位第31位划分,file_0_0存放该位为0的数字,file_0_1存放该位为1的数字。2.统计两个文件中数字个数,假设划分后各有30亿,则中位数在file_0_0中,因为要找的是第10亿。
重复上述过程,对目标文件依次按照二进制位进行划分。
1TB的长字符串数据,有1000个要匹配的字符串,怎么在单机情况下设计程序(腾讯)page 1
思路。1.分块读取,将1TB大文件分成若干个块,逐块读取或映射到内存处理。每块读取时保留前一块末尾一定长度(等于最大模式长度减一)的重叠数据,匹配跨块的串。2.在线处理,用Aho Corasick算法(原理是前缀树和自动机)批量构建串的匹配结构,流式读入文件内容并搜索。
假设有1000万个查询串,每个串的长度不超过255字节,去重后有300万个,请统计 最热门 的10个查询串,要求使用的内存不超过1G字节。page 161
有1万个数组,每个数组500个元素,并且有序。如何在这1万*500个数中找到 前500的数 ?
有10个文件,每个文件大小1G字节,每个文件每一行存放的是用户的query,每个文件的query都可能重复,要求按照query的 频率排序 。
有一个包含20亿个32位整数的大文件,内存2G字节,找到 出现次数最多的数 。
给定10亿个数字,从中找出 最小的100万个数字 ,假设内存足够容纳全部10亿个数字。(携程)
10亿个整数找最大的100个,怎么办?(腾讯)
有一个包含20亿个32位整数的大文件,内存2G字节,找到 出现次数最多的数 。(阿里)
有10亿个url,会出现重复,内存只有1G,如何统计出现次数最多的前100个URL(作业帮)
一个异常巨大的网络日志文件,日志记录来访者ip以及时间戳(即每一次访问都有一次记录),查找里面访问次数前10的ip。(shopee,腾讯)
给你10亿个数据,怎么找出重复次数最多的10个数?(腾讯)
分块。1000万个查询串,每个255字节,总共2550MB,超过1G字节,应分为4块,每个块250万个串。
统计频率。在每个块中用hashmap统计串出现频率,然后用最小堆得到当前块的top10查询串。
得到4个块的top10串后,再用最小堆,得到全局下的top10串。
import java.util.*;
public class TopKQueries {
public static void main(String[] args) {
//假设我们有一个查询串的列表
List<String> queries = getQueries();
//1.去重
Map<String, Integer> frequencyMap = new HashMap<>();
for (String query : queries) {
frequencyMap.putIfAbsent(query,0);
}
//2.统计频率
for (String query : queries) {
if (frequencyMap.containsKey(query)) {
frequencyMap.put(query, frequencyMap.get(query) +1);
}
}
//3.找出最热门的10个查询串
PriorityQueue<Map.Entry<String, Integer>> minHeap = new PriorityQueue<>(
(a, b) -> a.getValue() - b.getValue()
);
for (Map.Entry<String, Integer> entry : frequencyMap.entrySet()) {
if (minHeap.size() <10) {
minHeap.offer(entry);
} else if (entry.getValue() > minHeap.peek().getValue()) {
minHeap.poll();
minHeap.offer(entry);
}
}
//输出结果
while (!minHeap.isEmpty()) {
System.out.println(minHeap.poll().getKey());
}
}
private static List<String> getQueries() {
//模拟生成1000万个查询串
return new ArrayList<>();
}
}
100亿URL,每个 64 位,判断在不在黑名单(黑名单100亿URL,每个64位,判断给定URL是否在黑名单)(淘天)page 1
用布隆过滤器,如果内存占用过大,可以考虑外部存储,内存仅存储位数组一个切片。
给定输入文件,包含40亿个非负整数,设计算法,产生 一个不存在该文件中的整数 ,内存限制1G字节,怎么实现?如果再难一点,只用10M字节内存呢?(给定一个包含40亿个非负整数的文件,在内存非常有限(例如1GB,甚至只有10MB)的条件下,设计算法找出一个不在该文件中的整数。)page 16
40亿只鸟,每个鸟都有自己的id,经过一个信号塔会记录,信号塔容量为1G字节,晚上工作人员会带着id查询这个鸟今天是否经过,怎么设计?如果鸟更多呢?400亿呢?(有海量的对象(比如鸟),每个对象都有一个唯一的 ID。当对象经过一个传感器(信号塔)时,系统需要记录它是否出现过。由于记录设备的存储资源非常有限(仅有 1GB 空间),设计在极小的内存中保存这些记录,并能在查询时快速判断特定对象是否曾经过。)(腾讯)page 1
有一大堆重复int值,用位图获知不同int的数量,用位图需要提前分配足量内存,假如这些值很稀疏,那就会浪费大量内存,怎么改进?(金山办公)page 1
1G字节内存情况。采用 位图法 。因为整数的范围是0到2的32次方减一,我们要2的32次方个位,也就是512M字节。
具体步骤:初始化512M字节位图,所有位设为0,遍历文件中每个整数,将对应位图设为1,遍历位图,找到第一个为0的位,这个位的索引就是我们要找的缺失整数。
10M字节内存情况。分桶法。将证书范围分成多个桶,每个桶包含一部分整数。
具体步骤:
将整数的范围分成多个桶,每个桶大小为2的16次方,这样我们只要2的16次方个桶, 每个桶用一个整数表示桶内整数的数量 。桶的计数数组长度为2的16次方,每个整数4字节,总共256K字节内存。
遍历文件中每一个整数,统计每个桶内的整数数量。
找到一个桶,其内的整数数量小于桶的大小2的16次方,说明桶内有缺失的整数。
再次遍历文件,只处理落在这个桶内的整数,使用位图法找到具体缺失的整数。
如果内存限制更加严格,可以进一步细分桶的大小,或者采用多轮分桶法。
给定一个数组,包含从1到N整数,N最大为32000,数组可能有重复值,N的取值不定,若内存只有4K字节可用,如何打印数组中 所有重复元素 。(有一个包含100亿个url的大文件,假设每个url占用64字节,找出 所有重复url 。)page 59
4K字节意味着不能用数组或哈希表。可以用位图记录每个元素是否出现过。
4KB相当于32000多位,可以记录每个元素是否出现过。
解决方案。初始化一个32000位的位图,所有位初始化为0,。
遍历数组中每个元素,检查其在位图中的位:如果该位为0,将其设为1。如果为1,说明该元素已经出现过,将其打印出来。
public void printDuplicates(int[] arr) {
//创建一个32000位的位图
byte[] bitmap = new byte[32000 /8]; //每个字节8位,所以32000位需要32000/8字节
for (int num : arr) {
int byteIndex = num /8; //计算该元素在位图中对应的字节索引
int bitIndex = num %8; //计算该元素在字节中的位索引
if ((bitmap[byteIndex] & (1 << bitIndex)) !=0) {
//如果该位已经为1,说明该元素是重复的
System.out.println(num);
} else {
//否则,将该位设置为1
bitmap[byteIndex] |= (1 << bitIndex);
}
}
}
给你40亿个32位无符号整数,内存1G字节,找出所有 出现了两次的数 。page 59
由于内存限制,采用位图或哈希表解决。
创建一个大小为80亿位的位图,初始化为0,用2个比特记录每个数的出现次数。
遍历每个数,如果该数对应的位为0,则设置为1,如果为1,则设置为2。
再次遍历位图,找出所有值为2的位,这些位对应的数就是出现了两次的数。
如何快速统计大文件中某个单词的出现次数(阿里国际)
方法一,直接顺序遍历并记录次数,如果文件过大则分块顺序遍历。时间复杂度O n,且每次统计都要遍历。
方法二,先建立倒排索引,后续直接查询这个词典。建立索引时间复杂度O n,把每个单词和他的出现的位置列表记录下来。这样每次统计不需要遍历,时间复杂度O log V,其中V为词典大小。
1000w url排序,10M内存(一千万URL排序,十兆字节内存)page 1
外部排序。将数据分块写入多个临时文件,每块在内存进行内部排序,写回磁盘。然后用多路归并,从所有已排序的临时文件读取最小URL,用小根堆,建堆后把最小URL取出存入文件,再把每个临时文件的URL放入小根堆,逐步排序。
电商平台中订单未支付过期如何实现自动关单?page 1
定时任务。定期扫描数据库中超过指定时长的订单,更新状态。缺点为时间不精确,且订单量增加频繁扫描给数据库带来压力。
延迟队列。订单生成时设置延迟时间,超时后自动从队列取出,关单。缺点为无界队列,订单过多内存溢出。
redis过期监听。用redis key过期通知功能,在redis conf中开启notify keyspace events Ex,将订单以key存入redis。缺点为redis过期策略不能保证key立即过期。
redisson分布式延迟队列。基于rdelayedQueue,用z set和lua脚本实现原子操作。
rocketmq延迟消息。订单生成后,将订单信息以消息形式投放到rocketmq,设置延迟。延迟后消费者获取消息后检查订单状态,执行关单逻辑。缺点为要处理消息丢失、幂等性问题。
如何设计一个支持10万QPS的会员系统?(字节)page 1
ElasticSearch高可用优化。1.双中心主备集群架构。部署两个数据中心,机房A为ES主集群,机房B为备集群。所有读写都走主集群,通过MQ异步同步数据到备集群。主集群故障切换到备集群。2.流量隔离。将下单流程与营销活动流量分离,针对营销活动高TPS业务单独部署集群。3.优化。合理分配shard,合理配置线程数如CPU核心数1.5倍,单个shard数据量保持50g以内,对会员数据只采用keyword类型,查询使用filter而不是query。
redis。瞬间高并发如机票盲盒活动,缓存能缓解压力。但ES近实时性,更新存在延时,容易出现不一致,导致脏数据写入redis。解决方法:1.更新流程,更新ES时,先加上会员ID的2秒ttl分布式锁,再删除缓存。2.查询流程,如果缓存不存在,则查询ES,检查分布式锁状态,若存在锁直接返回,若不存在锁则加锁,查询ES并更新redis,释放锁。
2^32个qq号怎么记录上下线状态(腾讯)page 57
用位图。每个qq号对应一位,1表示在线,0表示离线。2^32个bit共512M内存,
存储方案。1.redis bitmap,用户上线时,将对应索引的bit置为1,下线置为0。查询通过偏移量读取bit,时间复杂度O 1。2.如果单机吞吐量不足,则将位图用一致性哈希分片。
设计一下qq群禁言功能。(腾讯)page 57
状态持久化。禁言信息不仅保存在内存中如进程定时任务,也写入持久化存储redis,为每个被禁言用户在某个群设置一个key,含ttl。
消息队列。单独依赖进程内定时任务存在进程重启后任务丢失问题。用支持延迟消息的消息队列如kafka,调度解禁任务。用户被禁言时,系统在消息队列中发布延时消息,延时设为禁言时长,到期消息被消费,解除禁言。
多角度校验。1.写时校验。用户尝试发消息时,从redis查禁言状态,如状态仍为禁言,则拒绝发送。2.读时校验。消费端,对消息进行二次校验。
请你设计一个id发号器,除了基本功能的实现,还需要考虑哪些点?(为什么你数据库的ID不用自增ID而是用雪花ID?)(设计一个 QPS 一百万的分布式数据库的订单号方案)(滴滴)page 108
分布式ID的主流实现方法(淘天)
解决方案。1.UUID。本地生成,性能高,但长度较长,且不易索引,而且连续性和有序性较差。2.雪花算法方案。将64位id分为时间戳、工作机器id(idc和机器)和序列号。优点是递增的,缺点是对时钟依赖,时钟回拨问题。3.数据库自增方案。用mysql自增特性,借助auto increment increment和auto increment offset在多机器分配号段,优点是简单,id递增,缺点是依赖数据库,成为性能瓶颈。4.优化方案。(1)segment。用数据库批量获取号段,用缓存减少对数据库访问,用双buffer,提前加载下一个号段。优点是能提供高QPS,缺点id连续,暴露业务量。(2)雪花算法。基于雪花算法,用Zookeeper自动分配工作机器id。
其他考虑的问题:
全局唯一性。
主从切换。需防止主从复制延迟导致的冲突,设计时要考虑让备库切换为主库时仍然保证ID不重复。
强一致持久化。用etcd保存当前ID的状态。
时钟和序列管理。1.时间戳。如ID包含时间戳,要防止机器时间回拨导致重复和单调性问题。2.序列号溢出。当时间戳精度有限时,设计序列号溢出策略。
设计一个停车场管理系统?(滴滴)page 57
系统架构。1.api网关层。对外暴露restful接口。2.业务微服务层。(1)车辆管理。负责车辆入场、出场记录、停车时长计算。(2)车位管理。管理车位状态、分配和预订。(3)订单和支付。生成订单,计算费用,对接第三方支付系统。(4)用户管理。用户注册、登录、会员管理。3.数据层。mysql,存储订单、用户、车辆和车位数据。
模块设计。1.车辆管理。(1)入场。用户车辆到达停车场,通过车牌识别系统输入车牌信息,调用车辆管理服务,记录入场时间,并请求车位分配,记录数据到数据库,更新车位状态为占用。(2)出场。用户出场时通过识别系统获取车辆信息,计算停车时长,并调用订单服务计算费用,用户支付后,更新订单状态和车位状态为可用。2.车位管理。(1)维护车位信息表,字段包含车位编号、所在区域/楼层、状态。(2)提供车位分配算法,优先分配最近的空闲车位,考虑会员预定。3.订单。(1)订单生成。入场时生成初步订单,记录入场时间、车牌、分配车位。(2)停车费用计算:根据停车时长、计费策略(按时计费、封顶收费等)计算金额。(3)支付接口对接:集成第三方支付(如微信支付、支付宝等)。
现在有一个线程池处理用户登录相关请求,线程池无法承受 应该采用什么拒绝策略什么任务队列?(美团)page 56
拒绝策略用CallerRunsPolicy,利用调用线程执行任务,用户登录是重要请求,不希望直接丢弃任务。任务队列用ArrayBlockingQueue有界阻塞队列。
在进行set语句的时候 undo log ,bin log ,redo log是怎么变化的(美团)page 56
先生成undolog。undolog在执行DML(insert、update和delete)操作时会实时写入。
再生成redolog。当set语句更新数据时,操作先在内存buffer pool进行。在事务提交时,redolog刷新到磁盘上。
binlog。set语句更新数据时,数据库根据配置日志格式生成binlog记录。事务提交时,binlog刷新到磁盘。
注:题目中的set语句指的是update … set。
如何设计秒杀系统?(携程,淘天)pagee 17 page 56
几万QPS抢100门课程怎么设计?(作业帮)page 1
架构。用户->NGINX->风控->秒杀服务->缓存->数据库,其中秒杀服务->消息队列,进行削峰填谷。
商品预约阶段。用分布式锁保证每个用户获得预约资格。用redis cluster保证可用性。
等待抢购阶段。将商品详情提前静态化,放到用户最近的CDN节点上。
商品抢购阶段。1.流量削峰。用消息队列对抢购请求削峰。2.库存扣减。将库存放入redis,用分布式锁保证一致性。
分库分表。订单表根据用户ID哈希取模,分散写压力。
热点数据怎么处理? 我们用redis做缓存比较多,可以通过 改动redis sdk定位redis热key 。思路是记录每个请求,定时把收集到的数据上报,然后由一个统一的服务进行聚合计算。定位到热key后,要放到缓存中, 并要写入到jvm内存一份 ,并设置一个过期时间,注意写入jvm内存的数据不能过多。
静态资源怎么处理? 如商品图片,css,js使用cdn。
怎么实现高可用 ? 集群化。避免单点风险。redis异步复制,redis哨兵。 限流。使用阿里Sentinel限制接口请求频率。 流量削峰。 使用mq避免后端服务被打垮 。 降级。优先保证核心业务,对一些服务进行降级。
怎么保证一致性?怎么保证不超卖? 采用下单扣减库存方案,采用lua脚本对redis上秒杀商品的库存进行原子操作。 余额扣减直接用数据库自带的排他锁select for update。
性能测试 使用jmeter进行压力测试。
设计一个分布式的远程过程调用RPC框架,你会如何实现?(腾讯,快手)page 56
网络模型选择。1.io模型。选择采用同步多路io复用如epoll。2.网络参数调优,我之前做过一个demo项目发现一个简单的空业务方法响应时间高达几十毫秒,经过tcpdump分析,原因在于nagle算法和DelayedACK。在socket开启tcp nodelay参数,禁用nagle算法,降低延迟。
序列化方案。1.在rpc框架中,每次调用进行两次序列化和反序列化,对整体性能影响大。2.JSON。适合数据量小场景。2.thrift,protobuf。数据格式更紧凑。
rpc调用过程。1.客户端将类名、方法名、参数序列化为二进制流。2.网络传输,io模型和网络参数需要优化。3.服务端反序列化后,用动态代理调用实际业务方法,将返回值序列化后发给客户端。4.客户端反序列化。
如何根据应用场景选择合适的消息中间件?(字节)page 52
从功能和性能两个维度考虑,但不让一个功能短板作为一票否决的因素,可通过其他方式实现。
功能维度。1.延迟消息,rocketmq和rabbitmq支持,kafka不支持。2.优先级队列。rabbitmq支持,rocketmq和kafka不支持。3.消息重试和消息过滤。rocketmq支持,kafka和rabbitmq不支持。
性能维度。1.单机吞吐量。kafka百万级,rocketmq十万级,rabbitmq万级。2.消息发送时延。kafka毫秒级,rocketmq和rabbitmq微秒级。
如何确保你的消息只被消费一次?(如何保证消息的幂等性?消息重复消费的解决方案?)(美团)page 52
生产端保证。1.消息重传。发送消息如超时则重试2到3次。会产生重复消息。2.生产端幂等。kafka0.11和pulsar支持,为每个生产者分配ID以及为每个消息生成唯一标识。
消息队列保证。1.异步刷盘问题。消息在消息队列中先写入操作系统的page cache,然后异步刷盘。在此期间宕机则丢失消息。2.集群和复制。集群部署,用ISR(in sync replicas)机制,保证消息在leader和Follower都写入才返回成功。
消费端保证。1.消费进度管理。接收消息、处理消息、更新消费进度三步骤,如处理过程中出现异常但消费进度已更新,导致消息丢失;反过来,如消息处理成功但消费进度未更新则重复消费。2.幂等。引入全局唯一消息ID,在处理前查询数据库判断该消息是否已处理。也可以用事务将处理消息与消费进度更新绑定。
实现微信扫码登录(腾讯)page 51
微信扫码登录分为“待扫描”、“已扫描待确认”和“已确认”三个阶段。
待扫描。1.流程。当用户在pc端发起登录请求时,服务端生成一个全局唯一二维码ID,通过二维码给用户。2.唯一性。二维码ID唯一。2.轮询。pc端启动定时器,轮询服务端,检测二维码扫描。3.规定时间二维码未扫描失效。
已扫描待确认。1.流程。用户用移动端扫描二维码后,移动端将二维码ID与已登录token发给服务端,服务端收到后,将手机端token与二维码ID绑定,生成一个临时token给移动端。
已确认。1.流程。用户在移动端确认登录后,移动端携带临时token访问服务端。服务端校验后,更新二维码状态,生成正式token给pc端。
如何用 Redis 统计用户访问量?(腾讯)
hash方式。对于每个访问,用hset将用户标识存入以uri和日期拼接成的key中。统计某天的访问量,只需要对该key用hlen命令。
bitset方式。用redis位图,每一位代表一个用户是否访问页面。对于未登录用户,用哈希算法将其标识转化为数字。用setbit标记访问,getbit查询用户是否访问,bitcount统计总的访问用户数。缺点是对于数据稀疏的场景,内存消耗大。
HyberLogLog。用pfadd添加用户,用pfcount得到统计结果。缺点是统计结果存在误差,且无法查询某具体用户是否访问过。
现在有一个店铺发布一些图片与视频,你需要设计一个同步接口将数据同步到其他店铺,考虑数据量很大的情况下,从各个方面去考虑。(设计一个接口,实现将店铺中的图片和视频数据同步到多个其他店铺,同时能够应对海量数据传输的挑战。)(美团)page 58
数据存储。对于图片和视频用分布式对象存储OSS。支持横向扩展。文件的元数据存储在mysql中。
同步机制。1.推送。如果是实时或准实时,用推送机制。当一个店铺发布时,直接将数据推送到其他店铺,用消息队列kafka解耦操作。2.拉取。对于同步的频率不高或周期性场景,用拉取模型。各个店铺定时从主店铺拉取数据。
异步处理。同步大量数据时,用异步处理避免阻塞主线程。如用异步队列Celery处理。也可以用批处理。
比如说 42 亿个 QQ 号,然后有 10 万行数据。那比如它这个数据量就比较大了,查阅效率比较低。那你要提升查阅效率的话,采用分库的方法,你觉得要怎么分?比如前5万行放到一个库里,然后5万行放到一个库里。这里有个问题,比如说想要查找名字叫做abc的所有账号,可能前五万行外行里边有 10 个,后五万个行里边有 3 个,然后你要查出名字叫abc的用户,你就要查两次?(假设有 42 亿个 QQ 号的数据存放在 10 万行记录中,为提高查询效率,我们把这 10 万行平均分到两个库(每个库 5 万行)。但是如果需要查找名字为 “abc” 的账号,由于这类账号可能分布在两个库中,就必须分别在两个库中查询,造成查询次数增加,怎么办?)page 1
全局二级索引。在全局层面维护二级索引,将用户名映射到用户id及所在分片,当查询用户名时,先通过全局索引找到相关分片和数据位置,然后直接定位到具体分片进行查询,避免全库扫描。
从前端页面到Java后台再到数据库,有一张表,表存在上百万条数据,从这三个层面,去做一个查询方面的优化,单表查询。(在一个三层架构的系统中(前端页面、Java 后台、数据库),如何对一张包含上百万条数据的单表进行查询优化?)page 1
前端。1.分页查询。不一次性加载所有数据,用分页,无限滚动分批次拉取数据,把不需要立即展示的数据如图片用懒加载方式。
Java后台。1.缓存中间层,用内存缓存或Redis存热点数据。2.对可以预先计算的查询数据用异步任务提前把数据存到Redis。
数据库。1.索引优化。(1)对主键索引用自增字段,保证数据顺序写入,减少页分裂和内存碎片。对于辅助索引要考虑能否索引覆盖,避免回表。注意联合索引顺序。(2)索引失效。重写SQL或使用hint比如force index强制使用索引。2.分库分表。结合mycat,shardingsphere。2.查询优化。(1)优化SQL。用慢查询日志和EXPLAIN FORMAT=JSON定位瓶颈,针对具体场景,设计和调整索引,如经常更新的记录用主键更新,避免用辅助索引更新,减少死锁风险。(2)事务和锁管理。长事务容易死锁,拆解长事务,针对死锁,设置合理的锁等待超时参数innodb lock wait timeout。写事务代码时,保证操作遵循固定顺序。(3)隔离级别。对允许脏读幻读场景,用读已提交减少间隙锁。
如何判断语言是面向对象的还是面向过程的?page 1
基本组织单元。面向对象语言以类和对象为组织单元,将数据和方法绑定在一起。面向过程语言以函数为组织单元,在C语言中,用结构体定义数据,用函数操作数据。
特性。面向对象语言强调继承多态、抽象,使代码复用拓展更容易。
实际代码。是否鼓励开发者用基于对象建模的思维组织代码。即使一个语言支持面向对象,但编程实践只是用面向过程的代码,那也没有发挥OOP的优势。
使用普通的互斥锁实现读写锁 page 1
go语言就是用互斥锁实现读写锁,允许并发读,写操作有排他性。有写操作等待或执行时,读会被阻塞。
实现。1.w,内部互斥锁,保证写互斥。2.readerCnt。持有读锁的Goroutine数量,当有写操作请求锁时,将此字段设置为负值,让读操作不能再进来。3.readerWait。当写操作正在等待所有读锁释放时,记录要等待的读者数量。4.writerSem,readerSem。阻塞等待的读和写。
问题。1.不可复制。go语言的rwmutex和互斥锁一样,内部有状态,不能复制。2.重入导致死锁,rwmutex不支持重入,读锁中调用写锁会被阻塞;递归调用累积读锁要等释放才能继续。3.释放未加锁的rwmutex,导致panic。
后端项目的集群部署,如果在使用canal同步数据库binlog的时候发生了宕机,从节点的同步方案?(在后端集群部署中,当使用 Canal 进行数据库 binlog 同步时发生宕机,从节点如何继续保持数据同步?)page 1
offset持久化。Canal解析binlog时,记录当前同步的binlog文件名和位置,要持久化存储在Zookeeper中,Canal实例宕机重启后,可从持久化的offset继续增量同步。
数据补偿。宕机导致binlog存在错位则应该进行数据校验。
跨系统实时同步数据,分布式事务是唯一的解决方案吗?(跨系统实时数据同步,必须依赖分布式事务吗?)(怎么用MySQL binlog和消息队列实现跨系统实时数据同步?)page 1
binlog捕捉数据变化。用Canal把自己伪装成MySQL从库,实时接受主库的binlog。
用mq数据解耦。由于下游业务系统对订单数据有不同的业务转换过滤计算逻辑,且数据量大,直接将binlog数据同步到各个系统出现性能瓶颈。因此将解析后binlog写入mq的主题中。
保证数据同步实时性,顺序性。如采取单分区,则保证顺序性,但同步程序并发能力无法提高。考虑实际业务并非所有数据更新都要全局有序,如只保证单个订单或者具备因果关系的数据的更新顺序即可。将mq设为多个分区,并行处理,把同一订单或有因果关系数据放到同一个分区。
如果服务和mq之前发送消息进行数据同步的过程意外暂停了,如何去排查?(如何排查服务与消息队列之间数据同步消息传输中断的问题?)page 1
日志和监控排查。1.看服务端是否有异常日志、连接超时记录,看异常类型,网络超时、拒绝连接、序列化错误。2.看消息队列日志,broker的错误信息、消费者组状态,通过普罗米修斯看消息积压、消费延迟、吞吐量指标。
版本兼容。服务端sdk与消息队列兼容性。
选课,课的人数不能超,人的时间段不能重(在选课过程中,每门课程的选课人数不能超过设定的上限,同时同一学生在同一时间段内不能选修多门课程)page 1
数据库设计。用三个表,课程表,学生表,选课记录表。记录表用学号+时间段唯一联合索引保证只选一门。
选课。先查询该学生在对应时间段是否已选课程,若没有再用乐观锁选课,并检查课程名额,若成功则向选课记录表插入。
并发量大用分库分表,读写分离,并用Redis减少对MySQL的直接访问。
设计表的时候,关联表和在一个表中加冗余字段关联各有什么优势(用关联表和用冗余字段关联数据,各有什么好处?)page 1
关联表满足第三范式,用外键约束保证一致,适合写多读少,复杂多表关联查询性能差。冗余字段用定时任务或消息队列保证一致,适合高频次读和OLAP报表场景,对实时性要求高。
单例模式有哪些实现方式?page 1
实现。1.饿汉式,构造函数私有,static final声明实例,getInstance方法返回实例,jvm类加载时线程安全,缺点是不支持延迟加载,启动就占用资源。2.懒汉式,加锁。构造函数私有,static声明实例,getInstance方法用synchronized static修饰。支持延迟加载,缺点是synchronized每次都加锁,性能瓶颈。3.双重检查dcl。构造函数私有,volatile static声明实例,getInstance方法用static修饰,用if-synchronized-if保证线程安全。优点是延迟加载且高并发下只有第一次加锁,实例要用volatile声明防止指令重排序。4.静态内部类。构造函数私有,getInstance方法返回静态内部类Holder的INSTANCE成员。静态内部类的成员声明为static final。优点是支持延迟加载,且线程安全。5.枚举单例,enum类型,优点是简洁,天然防止反射、序列化,缺点是不能携带参数构造。
为什么双重检查锁要加上volataile? page 1
不加volatile时,编译器可以把构造器初始化和把instance引用指向对象重排序,导致其他线程也会进入双重校验锁创建对象。
单例模式有没有线程安全情况(讲讲单例模式)page 1
单例模式是一个类在整个应用生命周期内,只有且仅有一个实例。应用场景如日志记录器、id生成器、配置管理器。
实现。1.饿汉式,构造函数私有,static final声明实例,getInstance方法返回实例,jvm类加载时线程安全,缺点是不支持延迟加载,启动就占用资源。2.懒汉式,加锁。构造函数私有,static声明实例,getInstance方法用synchronized static修饰。支持延迟加载,缺点是synchronized每次都加锁,性能瓶颈。3.双重检查dcl。构造函数私有,volatile static声明实例,getInstance方法用static修饰,用if-synchronized-if保证线程安全。优点是延迟加载且高并发下只有第一次加锁,实例要用volatile声明防止指令重排序。4.静态内部类。构造函数私有,getInstance方法返回静态内部类Holder的INSTANCE成员。静态内部类的成员声明为static final。优点是支持延迟加载,且线程安全。5.枚举单例,enum类型,优点是简洁,天然防止反射、序列化,缺点是不能携带参数构造。
线程、进程、集群唯一性。线程唯一,用threadlocal或map<threadid,instance>实现。进程唯一用单例模式,针对单个jvm进程。集群唯一,借助外部存储MySQL、Redis以及分布式锁(Zookeeper、redisson、etcd)协议保证。
服务器大量请求超时,怎么排查?page 1
服务器层面。看如有大量time_wait,改用长连接,或修改tcp_tw_reuse参数。以及用top命令看cpu load average系统负载。
应用层。考虑MySQL,分析慢查询日志,加缓存,以及考虑实际场景,如周期性负载变化,是因为缓存刷新是10分钟一次,而因查询内容越来越多,每次刷新耗时15分钟,导致刷新不完,拖入下一个周期。对慢SQL要考虑优化索引,优化查询语句,并检查kill超过阈值的会话。其次对NGINX超时后返回简化的静态首页。
栈溢出会对其他进程造成影响吗?page 1
不会有影响。在操作系统中,每个进程有独立的虚拟地址空间,栈溢出只影响发生溢出的进程本身。
操作系统用mmu管理虚拟内存,每个进程有独立页表,硬件禁止进程随意访问物理内存。
需要启动一个线程去完成某一个工作,耗时是不确定的,我需要设置一个超时时间,不管运不运行完都要返回,如何设计呢?(如何为启动的线程执行不确定耗时的任务设置超时,并在超时后强制返回?)page 1
用Executor service和future get。用线程池提交callable任务,获得future对象后调用future.get()方法设置超时时间,超时抛出timeoutException,捕获后用future.cancel(true)发出中断信号,将任务停止。
Completablefuture.orTimeout()方法。Completablefuture提供了ortimeout方法,直接在超时后向该future抛出timeout Exception。
如何利用事务消息实现分布式事务?page 1
在分布式系统中,常见场景如订单系统创建订单,异步通知购物车系统清理,两者操作不同数据库,如不特殊处理,很容易出现:1.订单创建成功,消息发出失败,购物车未清理。2.消息发出成功,订单创建失败,购物车误删。要保证创建订单和发消息都成功或都失败。
事务消息思路。对消息生产做事务处理,流程分三步。1.发送半消息,生产者将半消息发送到mq,消费者不可见。2.执行本地事务,提交或回滚本地事务。3.提交回滚事务消息,根据本地事务结果,向mq发起提交或回滚请求。
rocketmq的事务消息。rocketmq在上面三步基础上,增加事务反查。生产者要实现transactionListener,包含executeLocalTrasaction和checkLocalTrasaction。broker定期扫描半消息,调用checkLocalTrasaction。
RocketMQ 的事务消息是不是完整地实现了事务的 ACID 四个特性?(RocketMQ 的事务消息是否完全满足事务的 ACID 四大特性?)page 1
原子性是事务中全部操作要么全部完成,要么全部失败。rocketmq的消息投递或丢弃半消息,以及本地事务提交或回滚,要么全部完成,要么全部失败,符合原子性。
一致性。rocketmq异步操作,仅提供最终一致性,不满足。但要看业务对时效性的要求,如果时效性不强,他也是数据一致的。
隔离性。由于事物消息是分两步操作的,本地事物提交后,别的事物消息就已经可以看到提交的消息了。所以,不符合隔离性的定义。
持久性。半消息与提交后的最终消息均持久化存储在 Broker 的 CommitLog 中,并可配合多副本复制策略,保证持久性。
Redis能实现ACID属性吗?page 1
原子性。命令入队阶段未报错,执行阶段报错,Exec时仅该命令报错,其他命令还会执行,不保证原子性。
一致性。入队阶段错,事务被丢弃,保持一致性。执行阶段错,错误命令跳过,正确命令执行,保持一致性。
隔离性。exec前并发修改,默认不隔离,其他客户端可以读写同一key,用watch在exec前检测到被改,事务失败。exec调用后,Redis单线程执行,隔离性天然保证。
持久性。Redis是内存数据库。持久化依赖rdb,aof。如未配置持久化,则不保证持久性。如配置rdb,定时快照,事务提交到下一次快照前仍会丢失,不保证持久性。aof appendfsync no/everysec/always都有丢失窗口,不保证持久性。
即Redis保证一致性,隔离性。不保证原子性和持久性。
假如mysql和redis使用kafka解耦之后,有一部分失败导致数据不一致怎么办?(在将 MySQL 与 Redis 通过 Kafka 解耦后,若部分消息发送或消费失败导致缓存与数据库数据不一致,应该如何处理?)page 1
首先失败分为生产者发送失败和消费者消费失败。
在生产者端,开启idempotence保证幂等,不重复写入broker。
死信队列DLQ重试。将多次重试失败消息发送到DLQ topic,用自动化程序补偿。
最后,保留足够kafka消息存储期,将消费者偏移重置。
bitmap的作用,及常见使用场景 page 1
bitmap底层用Redis String字节数组存储,每个字节8个bit。命令如setbit,getbit,bitcount,bitop。
场景。1.用户签到,偏移量对应日期,统计连续n天签到用户数,多天bitop and,然后bitcount。2.uv计数,每个用户映射到一个固定偏移,一个页面一天维度一个bitmap。
对于微博的评论楼中楼功能,怎么设计数据库?(对于微博成千上万的评论,一个评论可能还会有很多回复,你会如何设计这个评论系统?)page 1
设计一个类似贴吧的系统,可以发帖 点赞 评论 关注等 考虑设计几个模块,设计几张表(shopee)page 1
表结构设计 我们要设计一个评论表存储所有评论信息,包含以下字段: comment id,评论的唯一标识符。 parent comment id,父评论的id。 content,评论的内容。 user id,评论者的用户id。 create time,评论的创建时间。 层级关系处理 为了处理楼中楼关系,我们可以用递归查询或者预计算路径的方式。 mysql递归查询,使用with recursive,获取某个评论所有子评论。 在评论表中增加path字段,存储从根评论到当前评论的路径。
性能优化 在parent comment id上建立索引。 扩展考虑 应对扩展,如点赞,回复提醒,预留extra data的json格式的字段,存储点赞数和是否已读。
CREATE TABLE comments (
comment_id BIGINT PRIMARY KEY AUTO_INCREMENT,
parent_comment_id BIGINT,
content TEXT NOT NULL,
user_id BIGINT NOT NULL,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
path VARCHAR(255),
extra_data JSON
);
CREATE INDEX idx_parent_comment_id ON comments(parent_comment_id);
CREATE INDEX idx_path ON comments(path);
在海量数据场景下,如何实现帖子按“热度”排序?以及如何高效存储与查询用户与点赞/关注关系?(你怎么对帖子按照最热进行排行?用户点赞/关注这个三元组(如果数据量很大)怎么存储查询?)(怎么设计计数系统?)page 1
热度排序用z set。
用Redis做全量计数存储,用主从和hash分片保证读写性能(hash分片优于按时间分片,因为越是最近发布的微博,计数的访问量越大),尽量将Redis作为单一数据源。对于明星微博被大量转发场景,先将+1操作写入MQ,消费端批量合并后再写Redis。
优化。1.定制化Redis,用扁平化KV结构,按weibo_id哈希的方式,将转发数、评论数、点赞数、浏览数以微博为单位存入大数组,去掉Redis中的冗余指针。2.冷热分离。热数据保留在内存,冷数据落盘至ssd,读取时再加载到内存中单独的cold cache中。
未读数系统。1.一对一未读场景,如艾特,私信,评论,以用户id为单位存储,点击后立即reset。2. 系统通知。所有用户共享一个消息列表,每个用户只记录 最后已读消息id或时间戳 ,那么未读数就是总消息数-已读偏移。
如何让主线程等待线程池中所有并发任务执行完毕,并一次性获取它们的返回结果?(我用了一个多线程去查多个结果集,主线程使用线程池获取多个结果集,主线程如何知道前面的线程执行完了,并且得到结果集?)page 1
对简单场景,用submit和future.get()。或用invokeAll。
对要按完成顺序处理结果场景,用Executor completion service。将其包装在Executor service上,提交任务后用take方法阻塞获取最先完成的future。
对异步场景,用Completablefuture allof。将每个任务封装成Completablefuture,allof创建一个Completablefuture,在所有子任务完成后完成,用join阻塞等待完成。
怎么快速定位到五分钟内重复登录了两次的QQ号,用什么数据结构 page 1
用bitmap。将5分钟滑动窗口拆分成5个位图,每个位记录在该分钟内发生登录的用户id。再用一个位图,记录重复登录的用户id。
计算当前槽。用floor(timestamp/60_000)%5,计算出属于哪个位图。
清空过期槽,将上次登录的槽直到这次登录的槽之间过期的清空。
标记。setbit login_bucket:idx $userid 1。
判断重复登录。用bitop and判断。
讲讲kafka page 1
consumer,消费topic的信息,一个topic可以让若干个consumer消费,若干个consumer组成一个consumer group,一条消息只能被consumer group中一个consumer消费,若干个partition被若干个consumer同时消费,达到消费者高吞吐目的。
partition,一个topic可以有若干个partition,从零开始标识partition,分布在不同的broker上,实现发布订阅时的负载均衡。producer用自定义规则将消息发送到对应topic下某个partition,以offset标识一条消息在一个partition的唯一性。
假设kafka中有一个 Topic,共 10,000 条消息,其中有 10 条坏数据。这些消息在 Topic 中如何分布到不同的分区(讲讲kafka分区策略)?page 1
1.range assignor区间分配。按消费者和分区索引排序后,给每个消费者分配一个连续区间,c0分配给p0,p1,c1分配给p2,p3,…,c4分配给p8,p9。2.round robin assinor轮询分配。将分区列表依次轮询分配给消费者。p0分配给c0,p1分配给c1,…,p4分配给c4,p5分配给c0等。3.按消息键保序策略。消息指定了一个业务相关的 Key,Kafka 默认会将相同 Key 的所有消息路由到同一个分区(Partition),并且保证在分区内部消息的写入与消费是严格有序的。
对于坏数据,如发送失败触发重试,kafka producer会把这个消息再次发向 同一个分区 ,不会重新走分区策略。
什么是 Consumer Group?如果一个 Consumer Group 有 5 个实例,Topic 有 10 个分区,Kafka 会如何将分区分配给这 5 个实例?page 1
若干个consumer组成一个consumer group,一条消息只能被consumer group中一个consumer消费,若干个partition被若干个consumer同时消费,达到消费者高吞吐目的。
分配。1.range assignor区间分配。按消费者和分区索引排序后,给每个消费者分配一个连续区间,c0分配给p0,p1,c1分配给p2,p3,…,c4分配给p8,p9。2.round robin assinor轮询分配。将分区列表依次轮询分配给消费者。p0分配给c0,p1分配给c1,…,p4分配给c4,p5分配给c0等。3.按消息键保序策略。消息指定了一个业务相关的 Key,Kafka 默认会将相同 Key 的所有消息路由到同一个分区(Partition),并且保证在分区内部消息的写入与消费是严格有序的。
为什么 Kafka 要引入分区(Partition)?page 1
水平可伸缩性。将一个topic拆分成多个partition,把他们部署在不同broker上,天然支持水平扩容。随业务增长,只需向集群中添加新broker,并把新分区分配给他们。
并行读写。生产者可以并行向多个partition写消息,consumer group也可以并行消费不同partition。
kafka消费者是按 Topic 来消费,还是按偏移量(Position)来消费?page 1
消费者是按topic名称订阅的,按偏移量消费。
消费者偏移量有重复消费问题,即C1消费了一部分数据,还没来得及提交这部分数据的位移就挂了。C2承接过来之后会重新消费这部分数据。C1消费P0,P1, C2消费P2,P3。如果C1从未提交,C1挂掉,C2开始消费P0,P1,发现没有对应提交位移,那么按照C2的auto.offset.reset值决定从那里消费,如果是earliest,从P0,P1的最小位移值(可能不是0)开始消费,如果是latest,从P0, P1的最新位移值(分区高水位值)开始消费。但如果C1之前提交了位移,那么C1挂掉之后C2从C1最新一次提交的位移值开始消费。
场景:设计一个网络服务器,现在有【多线程 + 每个线程内部阻塞IO】 和 【单线程 + Epoll】这两种方案,这两种方案在cpu负载,时间效率,内存资源占用这三个方面各有什么特点?现在有大量的就绪socket需要处理,使用单线程模型有什么问题?该怎么优化?如果让你来设计一个网络服务器,你有什么方案?page 1
1.(1)cpu负载。多线程内部阻塞io,内核要频繁在用户态和内核态切换,且在内核态轮询检查所有连接。而单线程epoll,用就绪队列机制,不用遍历所有连接。(2)时间效率。多线程内部阻塞io在延迟和吞吐上不如单线程epoll。(3)内存占用。多线程比单线程占用内存大。
2.大量就绪socket,如果单线程epoll采用水平触发模式,且一个就绪文件描述符进行耗时读写操作,事件循环会被阻塞,导致后续文件描述符得不到及时调度,发生饥饿。解决方法是对文件描述符加上epoll one shot,避免一个文件描述符阻塞。
场景:现在有一天内的大量日志,每条日志记录了用户id, 登陆时间,登出时间 {userid, login_time, logout_time}, 时间单位是秒。(1)怎么求出一天内的最大在线人数?(2)怎么求出维持在最大在线人数的最长持续时间?page 1
用差分数组和前缀和。因为只统计一天内,直接申请长为24*60*60的差分数组diff,初始全0,对每个记录{login_time,logout_time},直接将diff数组对应索引位置值加一和减一,最后对diff数组进行前缀和累加,下标t处即为第t秒在线人数。取前缀和最大的值即为最大在线人数。时间复杂度O n+T,n为日志条数,T为时间范围长度。空间复杂度O T。
网络编程中epoll,ET 触发模式下,比如说我去读数据,假设那里边有 10M 的数据,我读了 5M 的时候被信号中断了,那应该怎么做呢?(在 epoll 的 ET(边缘触发)模式下,假设缓冲区里一次性有 10 MB 数据可读,但你第一次调用 read 只读到了 5 MB 就因信号中断返回,剩下的 5 MB 数据该如何处理?)page 1
类似于netty的方案,在本地c代码中对read和recv调用进行封装,遇到中断EINTR时进行重试,在epollSocketChannel的unsafe.epollInReady回调方法中,创建一个循环重复读,直到遇到EAGAIN。
设计电商平台积分排行榜,实时(1秒)展示前一百名积分最高的用户(美团)
一个云端server,有100个接口,怎么统计每日接口调用量(滴滴)
假设开放平台有注册用户进来,管理员要给用户发站内信,你会设计几张表呀?具体怎么设计?两张表之间是怎么关联起来的?(设计站内信系统数据库表)(高德地图)page 1
表设计。用户表、站内信消息表、用户收件表。1.用户表存储用户信息,有用户id,用户名,角色如普通用户和管理员,仅管理员可发送站内信。2.站内信消息表存储站内信主题消息,包括消息id,标题,正文,发送者id,发送时间,过期时间,状态即草稿或已发送。3.用户收件表,存储每条站内信与接收用户的关系记录,管理员发送信息,在表内为每个接收用户插入一条记录,记录用户对消息的阅读和删除状态。
表之间的关联,用户1发送N个消息,1个消息记录N个收件表。
用户表
字段名 | 类型 | 说明 | 约束及索引 |
---|---|---|---|
id | BIGINT | 用户ID | 主键,自增 |
username | VARCHAR(50) | 用户名 | 唯一索引 |
role | TINYINT | 用户角色(0-普通用户,1-管理员) | 索引(按角色查询) |
created_at | DATETIME | 创建时间 | |
… | … | … |
消息表
字段名 | 类型 | 说明 | 约束及索引 |
---|---|---|---|
id | BIGINT | 消息ID | 主键,自增 |
sender_id | BIGINT | 发送者ID(外键 → User.id,仅管理员) | 索引 |
title | VARCHAR(255) | 标题 | |
content | TEXT | 消息正文 | |
send_time | DATETIME | 发送时间 | 索引(按发送时间查询) |
status | TINYINT | 消息状态(0-草稿,1-已发送) | 筛选草稿/已发送 |
expire_time | DATETIME | 过期时间 | 索引(过期消息过滤) |
deleted | TINYINT | 删除标志(0-未删除,1-已删除) | |
created_at | DATETIME | 创建时间 | |
updated_at | DATETIME | 更新时间 |
收件表
字段名 | 类型 | 说明 | 约束及索引 |
---|---|---|---|
id | BIGINT | 收件记录ID | 主键,自增 |
message_id | BIGINT | 消息ID(外键 → Message.id) | 索引 |
receiver_id | BIGINT | 接收者ID(外键 → User.id) | 索引(接收者查询) |
is_read | TINYINT | 阅读标志(0-未读,1-已读) | 索引(按未读查询) |
read_time | DATETIME | 阅读时间 | |
deleted | TINYINT | 删除标志(0-未删除,1-已删除) | |
created_at | DATETIME | 消息送达时间 | 索引(时间范围查询) |
一个大文件存储了上亿个或者更多的用户id,然后客户端要根据id请求用户详情,设计一个系统(腾讯)
假设用户详情数据来源是数据库,我们考虑用户id数据索引,数据库分片和缓存策略。
用户id索引方案。对于上亿级用户id,先对文件离线去重排序,按用户id哈希分片或范围分片。对文件中所有合法id建立布隆过滤器,拦截无效id。
请求流程。客户端经过api网关后,路由到用户服务节点,先在多级缓存中查询user detail: id key,如未命中,用旁路缓存模式,根据分片规则定位数据库节点,读取用户详情,返回给客户端,将结果写回Redis缓存。
数据库分片。用MongoDB,对用户id设置哈希分片或范围分片。
设计一个负载均衡算法:1. 负载均衡; 2. 同一个用户映射到同一个节点;3. 有节点的增删(腾讯)page 1
考虑具体业务场景,是应用在api网关,还是缓存,还是CDN。
一致性哈希算法和最高随机权重哈希算法。
一致性哈希算法,将整个哈希空间视为一个环,将每个后端节点映射到环上一个或多个点,用虚拟节点提高均衡性,将用户id哈希值映射到环上某个位置,然后顺时针找最近的节点。
最高随机权重哈希算法,对每个后端节点计算哈希值,取哈希值最大的节点作为目标,在节点变化时只需重新映射受影响的键,缺点是在查询时要对每个节点计算一次哈希,时间复杂度为O N。
设计转账接口,钱从A账户转到B账户,主要逻辑(shopee)
几百万个点如何求两点之间的最短距离,要求时间复杂度低于O(n^2)(滴滴)
先预处理。将所有点按x坐标排序,按y坐标排序得到两个数组。
递归划分,将x排序数组一分为二,y排序数组按照x排序数组的x坐标中位数也一分为二,然后递归的对这四个子数组进行求最小距离。
合并。从上面求出的两个最小距离取最小值,并在y排序数组中挑出所有距离x的坐标中位数距离小于最小值的点,再求距离,并更新最小值。
发红包应该考虑哪些问题,红包金额如何保证不会相差太多(滴滴)
刷新朋友圈,怎么设计查询(腾讯音乐)page 1
用发布订阅模式,客户端携带用户id,向服务端请求朋友圈数据。服务端用消息队列解耦,当一个用户发朋友圈时,异步的向所有可见的朋友圈的消息队列发送消息。
如何实现一个支持单机百万级连接的推送通知系统,支持在分钟级别实现亿级客户端的消息推送(腾讯音乐)
参考:https://time.geekbang.org/column/article/92960
如何设计一个支持亿级数据实时更新的24小时热搜排行榜,并优化Redis内存使用(淘天)
如果让你设计一个支持日交易量10亿条的电商平台资金与库存对账系统,你的逻辑是什么(淘天)
首先是对账订单和查询库存用并行处理,创建一个固定大小为2的线程池,分别用两个线程处理。然后是对比订单和库存check方法,最后将差异写入差异库save方法。用CountDownlatch实现在check方法前等待两个线程,在两个线程执行完毕后调用latch countDown方法,主线程中调用latch await方法实现对计数器等于0的等待。
其实还可以进行优化,对账订单和查询库存是并行了,但这两个查询和对账check和save之间还是串行的。明显,这两个查询和对账是可以并行的。两个查询是生产者,对账是消费者。用两个队列,订单查询操作将订单查询结果插入订单队列,查询库存操作将结果插入库存队列,这两个队列是一一对应的。用Cyclic Barrier,将初始值设为2,两个线程分别调用barrier await方法将计数器减1,此时T1和T2就可以执行下一条语句了,同时调用barrier的回调函数执行对账。
有一个app,他有一个日志系统,每一次请求(MAC)都会记录到日志中,一个用户可以进行多次请求(MAC),现在这个日志很大很大,而内存很小很小,同时服务器也是那种老式的,没有大数据工具、mysql之类的用,设计一种方案统计前k个出现次数最多的MAC?(美团)
将日志分块,先用哈希表统计出一个块每个MAC的出现次数,再用最小堆统计出一个块中前k个MAC,然后再在全局范围内用最小堆统计出现次数最多的MAC。
有一个两千万长度的数组存在磁盘中且不在同一个文件,如果通过下标快速定位到目标数?(元戎启行)
先将整个逻辑数组按固定大小切分为M个文件,假设每个文件存C个元素,那么对于任意下标i,可以用除法和取余直接算出要访问的是第几号文件,文件内的第几个元素。再用mmap内存映射文件。
mark end
假设redis能承受主5w qps,db能撑住500qps,db可能随机更新,如何把qps提升到20w,且保证一致性(在系统中,假设缓存(Redis)每秒最多能处理5万次请求,而数据库最多只能支持500次请求,而且数据库中的数据会随机更新。如何设计系统,使得整体的请求处理能力提升到每秒20万次,同时保证数据的一致性?)(网易)page 157
分级缓存:1.本地缓存:应用部署在多台服务器上,每个节点用本地缓存如caffeine。2.redis:将redis集群部署,用分片拓展。
缓存预热:db更新后,异步消息预加载数据到redis和本地缓存。缓存失效:1.写穿透:db写,同步更新redis。2.异步更新:读缓存若失效,去db读,用mq异步更新缓存。
限流:写操作QPS限500。对于频繁的随机更新,将他们聚合批量写db、更新缓存。
英语词典想存储怎么存?空间复杂度是多少?(sk小红书)page 114
用前缀树。 首先,前缀树的空间复杂度主要取决于两个方面:单词的数量和单词的平均长度。假设词典中有 ( n ) 个单词,每个单词的平均长度是 ( m ),那么前缀树的空间复杂度可以大致估算为 ( O(n \times m) )。
具体来说:
每个节点在最坏情况下会包含26个子节点(对应英文字母表中的每个字母)。 如果词典中的单词没有公共前缀,那么每个单词都会占用 ( m ) 个节点。 如果有很多公共前缀,节点的重复使用会让实际占用的空间减少。
怎么判断线程池的任务是否都执行完了?(阿里)page 1
用ExecutorService的invokeAll方法,他返回一个Future列表。
用CountDownlatch:每个任务完成时调用countDown方法,主线程调用await方法等待计数器清零。
用Future和isDone方法:提交任务后,获取Future对象,通过isDone判断是否完成。
Redis主节点挂掉了,哨兵没有选出下一个节点,这时候写的数据会怎么样?(百度)page 1
参考。
所有写操作无法完成,因为redis无法将这些写操作同步到从节点或新的主节点。
合理配置哨兵选举超时时间,故障转移尽可能快。
数据库表删除了10万条数据,但是表的磁盘占用没变化是为什么?(为什么表数据删掉一半,表文件大小不变)(百度)page 63
数据并没有物理删除。innodb数据用数据页存储。执行delete命令时,将数据页上记录标记为已删除。后续插入数据如果页内位置正确,可以复用已删除空间。
MVCC。innodb中,旧版本数据可能当前执行的事务还会访问。所有事务不引用这些行后,后台purge线程会清理,才是物理删除。
回收物理空间。要表重建,alter table t engine=innodb,optimize table t。
一张student表,其中字段id,age,name,其中id,age有索引,select * from student where age > 18,这个语句是怎么走索引的?(得物)page 1
索引扫描:用age的索引进行范围扫描range,从索引中找到第一个大于18的值,并继续扫描。使用索引下推,在扫描索引时过滤不匹配的记录,减少回表操作。由于 SELECT * 需要返回全表字段,因此在匹配记录之后仍需利用主键回表去聚集索引获取完整记录。
如果A线程插入一条记录,但是未提交事务,B线程执行select for update会怎么样?(得物)page 1
如果事务A插入了记录但未提交,这条记录会被事务A持有排他锁。事务B执行select for update会被阻塞,直到事务A提交或回滚。
A 线程插入记录,B 线程查询相同记录或B 线程查询区间。B看不到记录,且A获得索引的记录锁,B会被阻塞。
数据库有两个事务要修改数据库同一行数据一个字段,一个改为A,一个B。一个事务到达,开启了事务但没有commit,这时另一个事务到达,请问此时B事务会怎样。继续发问:在读已提交级别会怎样?上面两个事务的修改结果是啥,都有哪些可能结果?(腾讯)page 1
加记录锁,B事务会被阻塞,在读已提交和可重复读都被阻塞。
如果A事务成功提交,且B事务也提交,则字段先改为A再改为B。 如果A事务回滚,且B事务提交,则字段修改为A被撤销,恢复为初始值,再被修改为B。
现在A和B两个服务,流程为A服务向B发送请求,B处理请求并发送结果给A,A进行组装后将结果返回给客户端,如果B服务处理耗时从10毫秒延长为20毫秒,请问A服务QPS,CPU,内存将如何变化?(在由服务A和服务B构成的系统中,服务A向服务B发送请求,然后将B的处理结果组装后返回给客户端。现在假设服务B的处理时间从10毫秒增加到20毫秒,问题是:这会如何影响服务A的每秒请求数(QPS)、CPU占用和内存使用情况?)(京东)page 1
参考。 QPS。由于b服务处理时间增加了一倍,a服务的QPS会下降。假设a原本每秒可以处理100个请求,现在可能下降到50左右。
CPU使用率。取决于a服务并发能力,如果a服务线程池大小固定,单个请求的CPU消耗不增加,但由于请求处理数减少,CPU使用率会下降。
内存使用率。如果a服务在等待b服务响应的过程中需要缓存更多的请求或数据,内存使用率增加。
redis qps1万访问1个key和qps1万访问1万个key有什么区别?(在Redis中,10,000 QPS全部操作同一个键,与10,000 QPS分别操作1万个不同的键,性能和资源争用的表现有何不同?)page 61
- 访问单个key的性能
- 当 QPS 1万 访问一个key时,Redis的性能通常是非常高的。因为Redis是 单线程 的,它能够高效地处理单个key的读写操作。这种情况下,Redis的 内存访问 和 数据结构操作 都非常快,延迟很低。
- 访问多个key的性能
- 当 QPS 1万 访问一万个key时,情况复杂。首先,Redis需要处理更多的 网络IO 和 内存访问 。每个key的访问都会增加Redis的负担,尤其是在高并发的情况下,可能会导致 网络瓶颈 或 内存带宽瓶颈 。
- 此外,如果这些key分布在不同的 槽 或 节点 上,Redis还需要进行 集群间的通信 ,这会进一步增加延迟。
- 缓存策略的影响
- 在访问多个key的情况下,合理的 缓存策略 非常重要。例如,可以使用 批量操作 (如
MGET
)来减少网络IO的开销。 - 另外,如果这些key之间有 关联性 ,可以考虑将它们 合并 到一个数据结构中(如 Hash 或 Set ),这样可以减少key的数量,提高访问效率。
讲一讲MySQL 服务器怎么分析连接情况?有一大堆连接怎么处理?(MySQL连接数暴增怎么办?)(shopee)page 1
管理连接生命周期。1.用wait timeout参数自动断开空闲连接,如处于sleep状态且长时间空闲的连接。2.主动断开无效连接,遇到连接数暴增时,用show processlist判断哪些连接处于空闲。要区分事务中和事务外的连接,可以主动kill Connection关闭事务外的空闲连接。
优化连接方式。1.用连接池,保证连接复用,减少三次握手,权限验证,获取读写权限开销。2.持久连接。
调整max Connections参数要谨慎。提升该值会让系统接收更多连接,导致更多资源花费在连接验证和管理。
设计抢红包功能,要求金额随机,先来先领,不能超发。(shopee)page 1
实现一个抢红包的算法(游卡)page 1
架构设计。1.数据存储。红包记录表,含红包id,总金额,红包份数,红包状态。抢红包记录表,红包id,用户id,抢到的金额,时间。2.将红包剩余金额和剩余份数存到Redis,用lua脚本在Redis原子处理随机金额计算、扣减剩余金额和份数。
随机金额分配算法。1.初始化。总金额,总份数。2.抢红包算法流程。如果剩余份数为1,直接返回剩余金额。否则设定上限limit,(剩余金额/剩余份数)*2,生成一个随机金额money范围是0到limit。新的剩余金额为原剩余金额减去money。剩余份数减一。
git fetch和pull的区别?page 1
git fetch从远程仓库拉取最新提交记录,更新本地远程跟踪分支,不修改当前工作目录,不改变当前分支head状态。git pull相当于先执行git fetch,然后自动执行git merge,将远程直接合并到当前分支。
描述一下git三棵树。(百度)page 1
工作区树,暂存区树,版本库树。git add就是把工作区修改提交到暂存区,git commit就是把暂存区同步到版本库。
大文件小内存数据排序问题,两两归并比较慢怎么优化?多路归并的时间复杂度是多少?(腾讯)page 1
用多路归并排序,是一种外部排序的实践。 假设k个块,每个块包含n除以k个元素。 多路归并流程 分割,对每个分割出来的小文件进行内存内排序,可以用快速排序,堆排序。 归并,用多路归并将已排序的块合并成单一的排序文件。 归并的时间复杂度 初始化, 将每个块的第一个元素加入到最小堆中 ,涉及k次插入操作,每次插入时间复杂度为O log k,初始化时间复杂度是O k log k。 归并, 我们要合并总共n个元素,对于每个被合并的元素,执行以下操作 : 从堆中 取出最小元素 ,这个操作是O 1. 将这个元素所在的块的下一个元素插入到堆中 ,操作是O log k。 因此,每合并一个元素复杂度为O log k,总共n个元素,整个归并时间复杂度是O n log k。
java方法区OOM怎么排查?page 1
使用 jvisualvm 监控jvm内存使用情况,方法区oom一般是类加载过多,比如用反射或动态代理,或者类加载器没有被正确回收,或者jvm的metaspace大小配置过小。
设置了JVM Xmx为1G,但实际Java进程占用了1.3G,你觉得多出来的0.3G来自哪个部分?(为什么JVM设置最大堆内存为1G,但Java进程实际占用了1.3G内存?”)(百度)page 61
元空间和代码缓存Code cache,直接内存和本地方法有关,JVM本身进程也需要内存。
讲一讲一个活动有数据存在redis和MySQL中,现在活动下线了,先删除缓存还是先删除MySQL?(得物)
为什么先更新数据库再删缓存?(百度)
如果数据 一致性要求高 ,考虑 先删除MySQL 再删除redis,配合缓存预热或者加锁,因为可能出现缓存击穿的情况。 如果对 数据一致性要求不高 ,考虑 先删除redis 再删除MySQL,配合 延迟双删策略 ,因为有新的请求进来,可能会从MySQL中读到旧数据,并重新写入缓存,我们要删除缓存后,等待一段时间再删除一次缓存。
MySQL和redis的数据强一致性如何实现?(哈啰)
事务管理:用分布式事务管理器如Atomikos保证MySQL和redis在一个事务完成。
两阶段提交2PC:保证所有参与的数据库MySQL和redis保持一致。
现在有用户表,它的数据量很大,做了分表处理,现在有a和b两个字段,如何做到使用a或者b都可以查到某条数据在哪张表中?(在大量用户数据分表的情况下,如何保证用字段a或字段b都可以定位目标数据所在的具体分表?)(百度)
参考。 使用哈希算法进行分表,将a或b字段的值进行哈希计算,然后根据哈希值确定数据存储在哪个表中。
参考2。
映射表。用数据库表或redis存储他。记录字段a或b对应的表名。
全局唯一索引。保证a和b的全局唯一性,通过全局索引查询字段a或b,获取数据所在表名,然后进行数据查询。
linux看端口占用情况,有什么命令?(百度,腾讯,阿里巴巴)page 61
netstat -tuln查看监听的tcp和udp端口。
ss -tuln的性能更好。
lsof -i :8080。
多核情况下负载高低命令?(腾讯)page 1
top命令。关注load average值。显示最近1分钟,5分钟,10分钟的系统负载。结合CPU核心数判断负载是否过高,如系统是16核,负载值为10,则系统尚未达到瓶颈。
linux怎么找到一个程序对应的进程是多少?找到了这个进程号,然后我想去找这个进程有哪些线程怎么找?(快手)
ps aux | grep redis,ps -T -p pid。
Java栈溢出和堆溢出抛出什么类型的异常?(百度,蚂蚁,腾讯)page 1
堆溢出,异常类型是out of memory错误,当程序不断创建对象并且这些对象没有及时回收时,堆内存就会耗尽。
可以通过调整jvm堆内存大小比如-Xmx和-Xms增加堆容量。或者使用软引用以及弱引用。
栈溢出,异常类型是Stack overflow错误。栈内存用于存储方法调用的局部变量和执行上下文。当方法调用过深或方法内部大量局部变量时,栈内存会耗尽。可以调整方法实现,或者调整jvm栈内存大小-Xss来解决。
git怎么撤回上次的提交?page 1
用reset或revert,reset head^直接将head指到上次提交,历史提交不会出现放弃的提交记录,修改没有丢失,但要重新添加到暂存区,如果只是本地操作,用reset。revert是放弃指定提交的修改,但会生成一次新的提交,如果提交已经推送到远程仓库,用revert。
用rand7实现rand10(给定一个函数 rand7(),它能生成 1 到 7 之间均匀分布的随机整数。请你设计并实现一个新函数 rand10())(字节)page 1
思路,先生成更大的范围,(rand7减去1)*7 + rand7生成1到49的随机数,将rand7的结果视为两位七进制数,其中第1位乘以7。然后拒绝41到49的结果,拿到1到40的结果,用 (result减去1) % 10 + 1来映射到1到10的范围。
如果让你设计一个日志管理平台,你会如何设计,各个层面,持久层,缓存,控制层之类的都说一下。(腾讯)
持久层:MySQL,存储日志基本信息和元数据。对于大量的日志数据,用ES存储和检索。用Hadoop或Hbase进行日志的分布式存储和处理。
缓存层:用redis,存储热点日志数据。
控制层:用Springboot框架搭建控制层,提供RESTful接口,方便前端或其他系统调用。实现日志增删改查,提供日志实时查询和历史查询。
用swagger生成API文档,方便开发测试。
消息队列:用kafka处理日志异步流处理。将日志发送到消息队列中,由消费者处理。
// 使用Spring Boot搭建控制层
@RestController //定义一个RESTful控制器
@RequestMapping("/logs")//定义了API的基础路径
public class LogController {
@Autowired
private LogService logService;//业务逻辑层
@GetMapping("/{id}")
public Log getLogById(@PathVariable Long id) {
return logService.getLogById(id);
}
@PostMapping
public Log createLog(@RequestBody Log log) {
return logService.createLog(log);
}
@PutMapping("/{id}")
public Log updateLog(@PathVariable Long id, @RequestBody Log log) {
return logService.updateLog(id, log);
}
@DeleteMapping("/{id}")
public void deleteLog(@PathVariable Long id) {
logService.deleteLog(id);
}
}
如果让你设计一个日志收集器,你如何设计?日志收集器接入的服务节点非常多,你如何进行管理?如何在不影响业务情况下尽可能多发一些日志呢?(腾讯)
怎么设计 :
客户端组件,在应用程序中嵌入日志收集代码,将日志发送给中央收集器。
中央收集器,初步处理和存储。
存储系统:ES和MySQL。
服务节点非常多 :注册中心,分层管理。
如何尽可能多发一些日志 :
日志级别:在生产环境用info和warn。在调试和排查调整为debug或trace,获取更多信息。
异步日志记录:如Log4j2或Logback的异步Appender。
采样策略:对于高频日志,如每100条日志只记录1条。
import org.apache.log4j.Logger;
import org.apache.log4j.BasicConfigurator;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
public class LogCollector {
private static final Logger logger = Logger.getLogger(LogCollector.class);
private static KafkaProducer<String, String> producer;
static {
BasicConfigurator.configure();
producer = new KafkaProducer<>(KafkaConfig.getProducerProps());
}
public static void main(String[] args) {
logger.info("Starting log collection...");
// 模拟日志数据生成
for (int i = 0; i < 100; i++) {
String logMessage = "Log message " + i;
logger.info(logMessage);
sendToKafka(logMessage);
}
}
private static void sendToKafka(String logMessage) {
producer.send(new ProducerRecord<>("log-topic", logMessage));
}
}
如何把一个很大的文件在20分钟内把他处理并存入磁盘中?(得物)page 1
文件分割。将大文件 分割成多个小文件 ,可以并行处理,提高效率。用java RandomAccessFile 类,根据文件大小和可用内存将文件分割成多个块,每个块单独处理。 多线程处理。每个线程处理一个文件块。 ExecutorService 管理线程池。 异步存储。用Java的 completablefuture 实现异步存储,处理完一个文件块后,立即异步写入磁盘,不阻塞主线程。
假设有两个服务器分别在北京、上海,他们有一个同名文件,两个文件之间99.99%都是一样的,只有几行数据不一样,如何最快找到这几行?(如何快速定位两个几乎完全相同的大文件中那几处不同的行?)(得物)page 1
用差分传输算法rsync。rsync算法将文件划分成固定大小的块,对每个块计算一个快速的滚动校验和如alder-32以及一个更强的散列值如MD5。算法过程。1.在两台服务器分别对同名文件分块,计算他们的滚动校验和。2.交换校验和列表,远小于文件本身,并比较。如果某个块校验和不一致,则这个块有改动。3.对于校验和相同的块,不传输;对不一致的块,再进一步计算强散列值确认差异,再把实际内容传输。
要实现一个队列,要求固定长度,push和pop都是O(1),怎么实现?(设计一个固定大小的队列,使得入队(push)和出队(pop)操作的时间复杂度均为 O(1))(得物)page 1
用环形队列,在固定大小数组上操作,维护head tail指针,用取模运算循环利用数组空间。head指向队列头部元素,即最早入队的元素。tail指向下一个待插入位置。队列满的条件是tail+1对容量取余等于head,队列空的条件是head等于tail。入队先判断未满,再将新元素写入tail下标处,更新tail。出队先判断未空,再读取head指向的元素,更新head。
一个进程 20 个线程,fork 一次有多少线程(得物)page 1
40个线程。父进程有20个线程,子进程有20个线程。
有个超大文本文件,由单词组成,给定两个单词,找出这 两个单词在文件中的最小距离 ,就是相差几个单词,注意单词有重复。能在O n时间复杂度完成吗?
为了在O n时间复杂度完成,采取以下步骤: 遍历文本 ,一次遍历文本文件,记录两个单词出现位置。 双指针法。使用两个指针分别指向两个单词的位置列表,计算他们之间的最小距离。
双指针法。初始化两个指针,分别指向两个列表的开头。
计算当前指针指向的两个位置之间的距离。
如果第一个列表中的位置小于第二个列表中的位置,则移动第一个指针;否则移动第二个指针。 每次移动两个位置列表中更小的值 。
每次移动指针时,更新最小距离。
public int findMinDistance(String[] words, String word1, String word2) {
List<Integer> pos1 = new ArrayList<>();
List<Integer> pos2 = new ArrayList<>();
//记录位置
for (int i =0; i < words.length; i++) {
if (words[i].equals(word1)) pos1.add(i);
if (words[i].equals(word2)) pos2.add(i);
}
//双指针法计算最小距离
int i =0, j =0;
int minDistance = Integer.MAX_VALUE;
while (i < pos1.size() && j < pos2.size()) {
int dist = Math.abs(pos1.get(i) - pos2.get(j));
minDistance = Math.min(minDistance, dist);
if (pos1.get(i) < pos2.get(j)) {
i++;
} else {
j++;
}
}
return minDistance;
}
怎么设计一个高并发系统?(作业帮)
微服务拆分业务。
垂直分库水平分表。
数据库,redis,HTTP使用连接池。
MySQL主从分离。
用缓存,redis,本地缓存。
cdn。
消息队列,削峰填谷。
ES支持查询搜索统计。
熔断降级,限流。
压力测试。
线程池核心线程数,最大线程数都是2,有界队列,不能改参数,100个任务打进来怎么办?(京东)
参考。 使用任务队列拒绝策略,可以自定义,将任务持久化到数据库或消息队列中,稍后再处理。
任务拆分异步处理:将大任务拆分成多个小任务,通过异步处理,用多个线程池或消息队列分散压力。
使用外部队列:将任务放到外部队列如redis队列,通过另外一个线程池或定时任务消费。
以下是另一个参考答案: 引入阻塞队列BlockingQueue,暂存所有100个任务。
任务生产者将任务放入这个外部队列,不直接给线程池。
任务消费者启动专门线程,从外部队列取出任务并尝试提交给线程池。如果引发拒绝策略,要捕获异常,并重试。可以用指数退避或固定时间间隔,再提交。
我提供一个API来call你,你收到请求就丢到线程池里面,然后就返回给我“处理中”。你处理完以后再告诉我,调我的接口通知我。现在假设我这边发起一个1000qps的的服务,那么你要怎么去设计这个线程池的参数?题目给的信息有限,你可以再往里补充信息,比如它现在是个分布式的还是单机的集群?比如你可以考虑你要多少服务器,每个服务器要生成多少资源?假设SLA规定你一分钟一定要处理完给我响应,在此基础上反推各个参数是多少?(shopee)
由于一个请求内容比较大,被TCP分成了若干个数据包到达,所有数据包不是同时到达的,间隔时间大概几毫秒到几十毫秒,数据包到达后存储到缓存区,读取数据的线程是由线程池提供的,此时发生了gc后,可能会出现线程A读取了缓存区的一段数据还没读完,就被线程B读取了后一段数据,导致读取不完整,这种情况应该如何解决?(哈啰)
redis缓存穿透问题在数据库分库分表场景下怎么解决?(oppo)
数据分布不均:分库分表下,某些分片数据量可能远大于其他分片,如果缓存穿透发生在这些节点上,会导致数据库压力骤增,解决方法是对数据库监控,采用一致性哈希方法。
缓存失效策略:不能简单设置全局缓存失效时间。比如,根据分片的访问频率和数据量调整失效时间。对于访问频率高的分片,设置较长的缓存失效时间。访问频率低的分片,设置较短的缓存失效时间。
热点数据处理:增加缓存层级或用本地缓存。
分布式锁:为了避免缓存穿透多个请求同时打到数据库,可以用分布式锁控制。当某个请求发现缓存失效时,先尝试获取分布式锁,获取成功则访问数据库并更新缓存;获取失败则等待一段时间重试。
让你设计一个支持JAVA的rpc框架,你怎么实现?如何保证高可用,高性能,服务化?(腾讯)
核心组件。 服务注册与发现。使用zookeeper实现服务自动注册与发现。
序列化与反序列化,选择protobuf减少数据传输开销。
网络通信采用netty,保证低延迟高吞吐。
负载均衡如轮询,随机,一致性哈希。
性能优化。异步调用,使用completablefuture减少 线程阻塞,提高并发能力。
连接池管理,使用hikaricp高性能连接池。
缓存机制,引入redis,对热点数据进行缓存。
public interface HelloService {
String sayHello(String name);
}
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return "Hello, " + name;
}
}
public class RpcClient {
public static void main(String[] args) {
//创建代理对象
HelloService service = RpcFramework.refer(HelloService.class, "127.0.0.1",1234);
//调用远程方法
String result = service.sayHello("World");
System.out.println(result);
}
}
rpc怎么动态配置接口?(百度)
可以用Dubbo动态配置规则实现。
系统A调用系统B的服务,如果B的服务出错了应该如何保证系统A的正常运行?(哈啰)
异常处理:在系统A捕获B可能出现的异常并处理。
重试机制:A自动重试。
熔断:当调用B多次失败,暂停对B的调用,避免A受到过多影响。
降级:当B不可用时,用预先准备好的降级方案,保证A基本功能正常。
try {
// 调用系统 B 的服务
response = systemBService.call();
} catch (Exception e) {
// 处理异常,例如记录日志、通知管理员等
logger.error("系统 B 服务调用失败", e);
// 可以选择返回默认值或抛出自定义异常
return defaultResponse;
}
@Retryable(maxAttempts = 3, backoff = @Backoff(delay = 1000))
//Spring Retry 来实现重试机制
public Response callSystemB() {
// 调用系统 B 的服务
return systemBService.call();
}
@HystrixCommand(fallbackMethod = "fallback")
//Hystrix熔断
public Response callSystemB() {
// 调用系统 B 的服务
return systemBService.call();
}
public Response fallback() {
// 熔断后的降级处理
return defaultResponse;
}
public Response callSystemB() {
try {
return systemBService.call();
} catch (Exception e) {
// 降级处理
return fallbackService.call();
}
}
单表读写热点数据,假设后端系统到数据库网络io时间为1毫秒,如何支持1000qps的并发?(快手)
数据库索引优化,确保热点数据表有合适的索引,减少查询时间。
缓存机制,将热点数据缓存到redis中,减少对数据库直接访问。本地缓存,在应用层引入guavacache,减少对redis的访问。 客户端缓存,减少请求服务端。
异步处理,消息队列,将 写操作 放入消息队列如mq,异步处理写请求,减少对数据库的直接压力。批量处理写操作,减少数据库io次数。
数组和链表里面分别有100万个整数,循环去计算这100万的数字和,数组和链表哪个快?(网易)
数组中的元素在内存中是连续存储的,因此可以通过 索引 直接访问,时间复杂度为O 1,总的时间复杂度是O n。
- 链表 :链表中的元素是通过指针链接的,总的时间复杂度也是O n。 但是由于操作系统的cache机制遵循局部性原理,访问一个元素附近的元素会存放在cache中,因此数组比链表更快。
数据库表存的是余额,另外一个表存的是积分,怎么设计消费送积分?(拼多多)
参考。 余额表存储用户ID和余额,积分表存储用户id和积分。
消费送积分业务逻辑,当用户进行消费时,先从余额表扣除金额,再根据消费金额计算送的积分,并累加到积分表,消费和积分操作应放在一个 事务 中进行。
MySQL分库分表场景下,怎么查询数据?分库分表对数据库索引有什么影响?(百度,携程)
参考。
查询路由:确保查询发送到包含相关数据分片上。需要中间件如ES来解析并路由,减少查询广播。
分区键:应该是查询常用字段,且分布均匀,避免数据倾斜和热点问题。
跨分片操作的优化:跨分片查询或联合操作通常性能较差,因为要在多个分片上执行并汇总结果,应避免,或用分布式SQL引擎。
使用合适的框架,如ShardingSphere。
影响:
分库分表下主键使用UUID,全局唯一。
在分库分表的设计中,如果唯一索引不是分片键,可以在设计主键时包含分片键信息。例如,如果订单表的分片键是用户ID,那么可以将用户ID包括在主键中。这样,即使通过订单号进行查询,也能直接获取到分片信息。这种方法有助于优化查询效率。
数据库表有三个字段,订单id(时间+随机),用户id(随机),下单时间,用什么做主键,怎么建立索引?为什么(网易)
选择订单id作为主键,因为订单id是唯一不重复的。用户id要建立索引,因为需要查询某个用户的所有订单,如果没有索引需要全表扫描。下单时间需要建立索引,因为需要查询某个时间段内的所有订单。
在分库分表的设计中,如果唯一索引不是分片键,可以在设计主键时包含分片键信息。例如,如果订单表的分片键是用户ID,那么可以将用户ID包括在主键中。这样,即使通过订单号进行查询,也能直接获取到分片信息。这种方法有助于优化查询效率。
有一张表,表里除了ID主键之外,还有一个唯一字段,根据唯一字段做查询,他肯定会走索引吗?(根据唯一字段做查询,他肯定会走索引吗?)(网易)page 1
如果查询条件是精确匹配比如=,数据库会选择使用索引,但如果是范围查询,要看索引类型和数据库的实现,可以通过EXPLAIN FORMAT=JSON语句检查是否走索引。
可以考虑索引失效的五种情况,如发生了隐式转换,使用了LIKE模糊查询,使用了函数或表达式not in。
一个表有用户和时间两个列,现有3个需求 (1)根据用户查; (2)根据日期查; (3)根据日期和用户查; 问:怎么建立索引?(shopee)
参考。 应该分开建立用户和时间索引,如果采取联合索引(日期,用户),在查用户时,可能会导致不命中索引。
抖音查找我的关注和我的粉丝怎么设计数据库表?(快手)page 62
参考。
用中间表,双写。
设计拼多多砍一刀的业务实现,表结构的设计。(滴滴)
TODO
要设计一个转账接口,有一个账户表,从一个用户账户扣钱,给另一个增加钱,怎么设计?怎么处理不正常或极端场景?(腾讯)
参考。 事务管理:转账涉及两个账户的金额变动,必须保证两个操作同时成功或失败。用分布式事务管理。
并发控制:用悲观锁确保同一时间只能有一个事务能修改账户的金额。
接口设计:源账户ID,目的账户ID,转账金额作为参数,接口的返回值包含操作结果以及错误信息。
怎么处理极端场景:异常情况要设计合理的重试机制,在重试达上线后报警。用普罗米修斯,ELK监控运行状态。
如何让MySQL抗住百万级别并发查询?(小红书)
优化数据库结构。采用分库分表策略,将数据分散到多个数据库实例和表中。比如按照用户ID的哈希值进行分库,按照时间进行分表。可以采取一致性哈希算法解决不均衡问题。
使用缓存机制。引入redis缓存,将热点数据缓存起来。
优化SQL查询。通过索引优化,查询重写等方式,提高SQL查询效率。
负载均衡。使用Ng将请求分发到多个MySQL实例。
读写分离。将读操作和写操作分别路由到不同的数据库实例。
MySQL存储引擎Innodb的数据结构,可以用hashmap吗,索引用有序数组查询效率会怎么样,有什么问题(百度)
hashmap对等值查询很高效,但是由于内部无序不支持范围查询。并且存在哈希冲突问题。
有序数组查询可以用二分查找,很高效。但是插入删除成本高。
redis的热key吞吐瓶颈是100万,但是请求qps有200万怎么办?(小红书)
热点key拆分。将大的key拆分成多个小的key。
本地缓存。在应用层引入本地缓存,减少对redis直接请求,比如用guava cache。
redis集群。将redis部署为集群模式,利用redis cluster的分片机制,将不同的key分布到不同的节点上。
读写分离。将读请求和写请求分离,读请求可以通过副本节点处理,减轻主节点的压力。
请求的平均延迟为3毫秒,最大延迟为5毫秒,则线程池的keepAliveTime设置为多少合适?(小红书)
参考。 keepaliveTime应设置大于平均延时,确保连接在大多数情况下不被过早关闭,同时也应该小于最大延时,避免连接在极端情况下长时间保持,占用资源。可以选择折中的4毫秒。
要设置为5毫秒以上,如果发生5毫秒延迟的请求,会有非核心线程因为空闲被回收,导致线程频繁创建和销毁。
电商平台订单未支付如何实现自动关单?
使用Scheduled注解,定时任务实现。 或者使用xxl-job实现定时任务。 使用RocketMQ,在订单创建后,把订单作为一个消息投递到mq,将延迟时间设置为30分钟,30分钟后消费者可以消费到这个消息,检查用户是否支付了这个订单。
如何设计一个支持10万qps的会员系统?
系统架构 。选择分布式架构支持高并发,使用微服务将系统拆分成多个独立服务,每个服务负责特定功能,比如用户注册,登录,会员信息管理。
数据库 优化。采用nosql数据库,如 redis 和 mongodb ,处理高并发读写。redis做缓存,mongodb存储会员详细信息。
缓存策略 。采用多级缓存,采用本地缓存如guavacache来缓存 热点数据 ,使用redis缓存 全局数据 。
负载均衡 ,采用ng将请求均匀分发到后端多个服务实例上。
异步处理 。对于非实时操作,如 发送会员通知 ,采用异步处理方式,使用mq解耦系统。
监控。采用es或prometheus收集和分析系统的日志和性能。
如何在不停机情况下更新系统?
服务分为有状态服务和无状态服务。 无状态服务如处理查询逻辑的search planner,特点是每个请求完毕后资源会释放,不同请求之间没有依赖,可以通过水平扩展实现扩容。 有状态服务比如闲鱼搜索引擎,本身服务是无状态的,但是引擎内部保存了用于处理索引分区,增量进度的状态,通过新版本镜像新建引擎,然后做新旧引擎全量数据同步,增量数据同时向新旧引擎发送,新引擎上线,逐步扩大承接流量的比例。
有了解过MySQL容灾吗?
容灾的基本概念 首先,容灾是指在数据库系统出现故障时,能够快速恢复数据和服务的能力。MySQL容灾通常包括 数据备份 和 故障转移 两个主要部分。
数据备份 数据备份是容灾的基础。常见的备份方式有 全量备份 、 增量备份 和 差异备份 。全量备份是定期对整个数据库进行备份,增量备份只备份自上次备份以来发生变化的数据,差异备份则是备份自上次全量备份以来发生变化的数据。
故障转移 故障转移是指在主数据库出现故障时,能够自动或手动将服务切换到备用数据库。常见的故障转移方案有 主从复制 和 主主复制 。主从复制中,一个主数据库负责写操作,多个从数据库负责读操作;主主复制中,两个数据库都可以进行读写操作,但需要处理好数据一致性问题。
高可用性架构 为了提高MySQL的容灾能力,通常会采用 高可用性架构 ,如 MySQLCluster 。这些架构通过多节点同步复制数据,确保在单个节点故障时,其他节点可以继续提供服务。
监控与报警 容灾系统还需要有完善的 监控与报警机制 ,及时发现数据库的异常情况,并触发相应的容灾流程。常见的监控工具包括 Zabbix 、 Prometheus 等。
购物车里面的物品,有满减和优惠劵的时候价格会不同,如何去自动适配用满减还是折扣扣减还是跨店铺合并减,用什么设计模式?
使用 策略模式 解决,策略模式是一种行为设计模式,在这个场景中,我们可以定义不同的优惠策略,比如满减,折扣,跨店铺合并减等,然后 运行时根据用户购物车内容自动选择最合适的策略 。 定义策略接口,这个接口包含一个计算最终价格的方法。策略上下文,负责在运行时选择合适的策略。自动适配策略,根据用户购物车内容自动选择最合适的策略,比如我们可以根据购物车商品数量,总价,店铺数量决定选择哪个策略。
public interface DiscountStrategy {
double calculateFinalPrice(List<Item> cartItems);
}
public class FullReductionStrategy implements DiscountStrategy {
@Override
public double calculateFinalPrice(List<Item> cartItems) {
//实现满减逻辑
return finalPrice;
}
}
public class DiscountStrategyImpl implements DiscountStrategy {
@Override
public double calculateFinalPrice(List<Item> cartItems) {
//实现折扣逻辑
return finalPrice;
}
}
public class CrossStoreReductionStrategy implements DiscountStrategy {
@Override
public double calculateFinalPrice(List<Item> cartItems) {
//实现跨店铺合并减逻辑
return finalPrice;
}
}
public class DiscountContext {
private DiscountStrategy strategy;
public void setStrategy(DiscountStrategy strategy) {
this.strategy = strategy;
}
public double applyDiscount(List<Item> cartItems) {
return strategy.calculateFinalPrice(cartItems);
}
}
public class DiscountService {
public double getBestDiscount(List<Item> cartItems) {
DiscountContext context = new DiscountContext();
//根据购物车内容选择最合适的策略
if (isEligibleForFullReduction(cartItems)) {
context.setStrategy(new FullReductionStrategy());
} else if (isEligibleForDiscount(cartItems)) {
context.setStrategy(new DiscountStrategyImpl());
} else if (isEligibleForCrossStoreReduction(cartItems)) {
context.setStrategy(new CrossStoreReductionStrategy());
}
return context.applyDiscount(cartItems);
}
private boolean isEligibleForFullReduction(List<Item> cartItems) {
//判断是否满足满减条件
return true;
}
private boolean isEligibleForDiscount(List<Item> cartItems) {
//判断是否满足折扣条件
return true;
}
private boolean isEligibleForCrossStoreReduction(List<Item> cartItems) {
//判断是否满足跨店铺合并减条件
return true;
}
}
``````java
public class DiscountService {
public double getBestDiscount(List<Item> cartItems) {
DiscountContext context = new DiscountContext();
//根据购物车内容选择最合适的策略
if (isEligibleForFullReduction(cartItems)) {
context.setStrategy(new FullReductionStrategy());
} else if (isEligibleForDiscount(cartItems)) {
context.setStrategy(new DiscountStrategyImpl());
} else if (isEligibleForCrossStoreReduction(cartItems)) {
context.setStrategy(new CrossStoreReductionStrategy());
}
return context.applyDiscount(cartItems);
}
private boolean isEligibleForFullReduction(List<Item> cartItems) {
//判断是否满足满减条件
return true;
}
private boolean isEligibleForDiscount(List<Item> cartItems) {
//判断是否满足折扣条件
return true;
}
private boolean isEligibleForCrossStoreReduction(List<Item> cartItems) {
//判断是否满足跨店铺合并减条件
return true;
}
}
redis 删除100万个具有相同前缀的键,他是怎么做的?
使用scan命令,遍历所有符合条件的键,用del删除,scan match “prefix:*“找到符合前缀的键。
如果让你设计一个分布式锁,你怎么弄?
实现方式 使用数据库的唯一索引或行锁实现。 基于redis的setnx命令实现。 锁的粒度 粗粒度锁适合竞争不激烈场景 细粒度锁可以提高并发性能 锁的过期时间 固定过期时间和动态过期时间。
高并发卖5000张优惠券的设计?
需求拆解和选型 配置券,涉及到券模版的创建,券模版有效期和库存消息。 发放券,涉及券记录的创建,过期时间和状态。 选择MySQL+redis+RocketMQ。 表结构 券模版字段: meta_no,模版号。 type,优惠券类型,如折扣券,现金券等。 valid_start_time,有效开始时间。 valid_end_time,有效结束时间。 status,券的状态,包括激活,停用。 stock,券的库存。 receive_rule,领取规则,定义了用户什么条件可以领取。 consume_rule,消费规则,如最低消费金额。 券记录字段: coupon_no,券号。 coupon_meta_no,关联到券模版号。 receive_out_no,领取流水号,如何时领取的唯一标识。 consume_out_no,消费流水号,记录在哪一笔交易中被使用。
订单表每天新增100万数据,分库分表方案怎么设计?
一天100万数据,一年有3亿数据,做一些空间上的预留,假设有5亿数据,我们可以按照16个分库,每个库32个表规划,一共512张表,每个表存放100万数据量,选择oid作为分片键,采用 一致性哈希算法 进行路由。 分表的依据 用oid作为分片键,首先oid对16取余,看映射到哪个库,然后oid除以16,再对32取余,看映射到哪个表。 给出uid,要查总金额 采用基因算法,确保uid对应的oid路由到同一个库或表,在生成oid时,把uid拼接到oid中。 一致性哈希算法 一致性哈希算法通过将数据和节点映射到一个固定大小的哈希环上,实现数据均匀分布和节点动态添加和删除。
数据分布:数据项哈希值决定他在哈希环上的位置,然后顺时针找到最近的节点,该节点负责存储这个数据项。
一个表第一年有1亿,第五年有10亿条数据怎么分库分表?(作业帮)
参考。
假设一个表存储2000万条数据,则10亿条数据应该要50张表,做空间上的预留,假设64张表,则可以分8个库,每个库8张表。
现有一个外卖系统,一天1000万条数据,用户需要查到近30天的数据,商家也要查询到近30天的数据,如何优化?
数据分区和索引优化。首先考虑对数据进行分区。比如按天或者周对数据进行分区存储。同时,对查询频率高的字段建立索引,比如用户id,商家id,订单时间。 其次,可以引入缓存机制,如redis,特别是对于热点数据,如某个商家的订单数据,可以缓存起来,减少数据库查询压力。 在数据库层面,可以使用读写分离或分库分表的策略,提高系统并发处理能力。
DDD是为了解决什么问题的?用DDD怎么划分领域?(阿里)
DDD领域驱动设计是为了解决复杂业务逻辑提出的。他将复杂业务逻辑分解成多个领域。
领域划分。 核心域,这是系统的核心业务逻辑,通常是最复杂最有价值的领域,如电商系统的订单管理和支付管理。 支撑域,支撑核心域的运作,本身不直接产生业务价值,如用户认证和权限管理。 通用域,可以被多个系统复用,比如日志记录和异常处理。
1亿个任务,CPU密集型,一个开9个线程,一个开20个线程,实际的物理线程都是8的服务器,这两个服务器哪个执行的快?
9个线程的更快,对于cpu密集的任务,线程数量应该和核心数量接近,否则会发生过多的线程调度。 如果是io密集型呢? 20个线程的更快,因为他允许应用程序在等待io时进行更多的处理。
一个用户同时登录两台设备,怎么让另一台用户退出登录?
每个用户登录后会生成一个唯一的会话标识符,存储在cookie或token中。 并发登录检测 当用户在第二台设备登录时,系统要检测这时一个新的登录请求,先检查是否已经有一个活跃的会话,如果存在标记为过期。为新的设备生成一个新的会话标识符。 通知旧设备 如果设备支持,通过websocket或pushnotification发送通知,告诉被下线。前端应用可以定期检查会话状态,如果会话无效,提示用户重新登陆。
qps和tps区别?
定义 qps是每秒查询率,比如一个api每秒能处理多少个请求,tps是每秒事务处理量,比如一个电商系统每秒能完成多少次下单。 应用场景 qps衡量读频繁场景,如搜索引擎,api服务。tps衡量写频繁场景,如电商、金融。
怎么设计一个数据库连接池?(阿里)
连接池的结构 连接池是一个连接管理器,负责创建,管理和回收数据库连接,可以用 线程安全 的集合ConcurrentLinkedQueue来存储这些连接。 连接池的初始化 启动时,预先创建一定数量的数据库连接,放入连接池中。可以设置最小连接数,最大连接数。 连接的获取与释放 当应用程序需要一个连接时,从连接池获取,如果没有可用,则选择 等待 或者是 创建新的连接 。使用完后,应用程序应将连接 释放回连接池 ,而不是直接关闭它。
数据库连接池参数有哪些?根据什么设置合理的连接池参数?(阿里)
连接池大小:影响数据库并发能力。
最小连接数:连接池空闲状态下保持的最小连接数。
最大连接数。
连接数=(核心数*2)+有效磁盘数。如4核i7,连接池大小为4*2+1=9。
连接超时时间:如果超过时间未获取到连接,抛出异常。
空闲连接超时时间:空闲连接的保持时间。
注册中心的底层实现是什么?
服务发现机制 服务在启动时会向注册中心注册自己的信息,如IP地址,端口号等。客户端要调用某个服务时,会向注册中心查询该服务的消息,然后调用。如像Eureka,提供了RESTfulapi来实。现服务的注册和发现。 数据存储 注册中心要存储服务的信息,通常用 分布式存储 保证高可用性和一致性。比如Eureka采用内存存储。在分布式系统中,数据的存储和同步是个关键问题,通常涉及到 CAP定理 的权衡。 健康检查 如果某个服务不可用, 注册中心将其从服务列表中移除,检查的方式可以是 心跳机制 ,也可以是 http,tcp请求 。 负载均衡 客户端在获取服务列表后,可以根据某种策略选择一个服务实例进行调用,比如随机或轮询等。
设计一个限流器。(字节)
系统架构设计:网关服务器,限流器(部署在网关服务器),配置中心(远程配置管理),redis服务器,存储计数。
流程:用户请求通过负载均衡到达网关服务器,若未达到限流,则转发到相应的微服务,若请求达到限流,直接返回503。
配置管理:用YAML配置。远程配置优先级高于本地配置。
Url:/
rules:
- actor:device
unit:second
rpu:10
algo:TB
scope:global
- actor:all
unit:second
rpu:50
algo:W
scope:local
限流策略:全局限流:对所有请求限流。账号限流。设备限流。资源限流,针对特定URL限流。
限流算法:固定窗口算法,滑动窗口算法,漏桶算法,令牌桶算法。
拓展设计:遵循开闭原则。插件化架构:定义限流策略统一接口,新增策略只要实现接口并注册即可。
注:策略是做什么和为什么,算法是怎么做。
你觉得安卓垃圾回收用的什么策略(小红书)
用的分代垃圾回收策略,对象分为新生代和老年代,新生代用复制算法,老年代用标记清除或标记整理算法,减少停顿时间。
安卓系统内存分配通常用TLAB,每个线程有自己的分配缓冲区,减少线程竞争。
多个进程的退出顺序,比如小红书应用,系统应用都是在后台,这个清理进程的话顺序是什么?依据是什么?(小红书)
资源释放顺序。进程退出时,应按照资源的使用顺序逆序释放资源,比如进程A使用了进程B释放的资源,那么进程A应该在进程B之前退出。
假设有一个10万的数据请求,你会有什么方法来实现这些数据的增删改查?
数据分片。将数据分散到多个数据库实例或表中,这样可以 提高并发处理能力 ,比如可以根据用户id哈希值决定数据存储的位置。或者使用一致性哈希算法。
缓存机制,比如用redis对于频繁访问的数据,缓存到redis中,特别是在读多写少的情况下,缓存的效果非常显著。
批量操作 ,对于增删改查,尽量采用批量操作,减少数据库连接次数。
异步处理 ,对于非实时的操作,比如数据 删除或更新 ,通过消息队列如kafka或mq,将操作放到队列中。
索引优化。在大数据量情况下,索引优化非常重要。
在多并发的情况下,如果我需要记录程序运行中的信息,但又不能耽误主线程的运行,应该怎么做?(哈啰)
用异步日志记录:将日志记录操作放在异步线程中,主线程继续执行不阻塞。
用消息队列:将信息放入MQ中,由消费者线程处理。
用线程池:管理日志记录。