牛客收集的八股知识点


如果是小林有的,我会直接标注看小林

Mysql

怎么用mysql实现乐观锁、悲观锁

乐观锁简单,就是cas呗

悲观锁:使用select for update实现吧

慢查询的原因

  1. sql没加索引
  2. 索引失效
  3. 单表数据量太大,3层的b+树可以放两千一百万+的数据
  4. join的数据量较大,如果 join 的数据量比较大时,mysql 会采用在硬盘上创建临时表的方式进行多张表的关联匹配,这种显然效率就极低,本来磁盘的 IO 就不快,还要关联。
  5. delete+in不走索引,sql没有进行这个的优化

如何定位慢查询

  1. 通过slow_query_log 设置是否开启慢查询日志
slow_query_log : 开启或关闭慢查询日志。

slow_query_log_file: 指定生成的慢查询日志路径(未设置则和默认和数据文件放一起)。

long_query_time : 慢查询记录时间阈值,SQL执行超过此时间则会被记录到日志(单位:秒,默认10秒)。

log_queries_not_using_indexes : 是否记录未使用索引的SQL
  1. 通过explain,得知sql的执行计划,并且针对性优化

SQL注入预防

这个应该不太会问

  1. 避免拼接SQL语句
  2. 对输入验证和过滤

Mysql分库分表如何实现?

  1. 确定分库分表策略
    • 垂直分库:根据业务模块将表分配到不同的数据库中。
    • 水平分库:根据某种规则将同一个表的数据分布到不同的数据库中。
    • 垂直分表:根据字段的访问频率或字段长度将表的字段拆分到不同的表中。
    • 水平分表:根据某种规则将同一个表的数据拆分到多个表中。
  2. 选择分片键:选择一个或多个字段作为分片键,用于决定数据在各个数据库或表中的分布。常见的分片键包括用户ID、时间戳等。
  3. 设计分片规则
    • 范围分片:根据数值范围将数据分布到不同的表或库中。
    • 哈希分片:使用分片键的哈希值来决定数据的分布。
    • 列表分片:根据分片键的枚举值将数据分布到不同的表或库中。

如何搭建Mysql主从集群

  1. 配置MySQL主服务器:需要在主服务器的配置文件中设置server-id,开启二进制日志log-bin,并设置log_bin_index指向索引文件
  2. 创建MySQL用户并授权:在主服务器上创建用于从服务器连接的MySQL用户,并授予复制相关的权限
  3. 配置MySQL从服务器:在从服务器的配置文件中设置server-id,不能与主服务器的server-id相同。以及read_only属性来防止在从服务器上执行写操作
  4. 设置主从复制:在从服务器上执行CHANGE MASTER TO语句来指定主服务器的连接信息
  5. 启动复制进程:在从服务器上执行START SLAVE;命令来启动复制进程

两个数据库怎么保证事务

不会,分布式事务也不是两个数据库啊

悲观锁sql怎么实现

select for update 对记录加锁,阻塞其他线程

更新完数据库再更新redis不行吗,除了白白浪费开销,还有什么问题

在实际业务中,缓存的数据可能不是直接来自数据库表,也许来自多张底层数据表的聚合。比如上面提到的商品详情信息,在底层可能会关联商品表、价格表、库存表等,如果更新了一个价格字段,那么就要更新整个数据库,还要关联的去查询和汇总各个周边业务系统的数据,这个操作会非常耗时。

从另外一个角度,不是所有的缓存数据都是频繁访问的,更新后的缓存可能会长时间不被访问,所以说,从计算资源和整体性能的考虑,更新的时候删除缓存,等到下次查询命中再填充缓存,是一个更好的方案。

联合索引在b+树怎么存的?

订单表和产品表,查找卖出1千元的以上的产品

用子查询可以用where,

SELECT product_id, product_name, total_sales
FROM (
    SELECT p.product_id, p.product_name, SUM(o.quantity * o.price) AS total_sales
    FROM orders o
    JOIN products p ON o.product_id = p.product_id
    GROUP BY p.product_id, p.product_name
) AS product_sales
WHERE total_sales > 1000;

怎么减少回表

  1. 聚簇索引
  2. 查询的列在联合索引当中

MySQL 中我有两张表,订单表有 2000 万数据,用户表有 100 条数据,如何让我的查询(订单信息带着用户信息)能够更快

Redis

解决hash冲突的方法

链式方法:就是用链表

开放地址法:

  • 线性探测,直接一直往后寻找,步长为1
  • 步长为2的n次方

如何设计热门商品排行

不知道

有没有规定redis节点有多少个

The cluster’s key space is split into 16384 slots, effectively setting an upper limit for the cluster size of 16384 master nodes (however, the suggested max size of nodes is on the order of ~ 1000 nodes).
集群的键空间被划分为16384个插槽,有效地为16384个主节点的集群大小设置了上限(然而,建议的最大节点大小约为1000个节点)。

为什么需要集群

  • 分片集群诞生理由:写性能在高并发下会遇到瓶颈&&无法无限地纵向扩展(不划算)
  • 分片集群:需要解决「数据路由」和「数据迁移」的问题
  • Redis Cluster数据路由
    • Redis Cluster默认一个集群有16384个哈希槽,哈希槽会被分配到Redis集群中的实例中
    • Redis集群的实例会相互「通讯」,交互自己所负责哈希槽信息(最终每个实例都有完整的映射关系)
    • 当客户端请求时,使用CRC16算法算出Hash值并模以16384,自然就能得到哈希槽进而得到所对应的Redis实例位置
  • 为什么16384个哈希槽:16384个既能让Redis实例分配到的数据相对均匀,又不会影响Redis实例之间交互槽信息产生严重的网络性能开销问题
  • Redis Cluster 为什么使用哈希槽,而非一致性哈希算法:哈希槽实现相对简单高效,每次扩缩容只需要动对应Solt(槽)的数据,一般不会动整个Redis实例

linux

查看内存的命令

free 命令用来显示系统内存状态,包括系统物理内存、虚拟内存(swap 交换分区)、共享内存和系统缓存的使用情况

linux怎么查看进程情况

ps

  • ps aux:显示所有进程的详细信息。
  • ps -ef:以标准格式显示所有进程的信息。
  • ps -u 用户名:显示指定用户的进程。

top

  • 实时显示系统中各个进程的运行情况,CPU和内存的使用情况等。

linux怎么查看cpu情况

top - 显示实时的系统进程和资源使用情况

fork和exec有什么区别

fork

  1. 创建一个新的子进程,这个子进程是父进程的副本。
  2. 子进程获得父进程数据空间、堆和栈的副本。
  3. 在父进程中,fork()返回子进程的PID;在子进程中,fork()返回0。
  4. 子进程从fork()的位置开始执行。

exec

  1. 用新的进程镜像替换当前进程镜像。
  2. exec()调用成功后不会返回,因为整个进程都被新程序替换了。
  3. exec()只有出错时才返回,返回-1。

Linux网络抓包用什么命令来实现?

tcpdump:tcpdump 是一个非常强大的网络抓包工具,可以用来捕获并分析网络数据包

如何排查服务器cpu占用率高的程序

1.使用top 定位到占用CPU高的进程PID top

2.通过ps aux | grep PID命令 获取线程信息,并找到占用CPU高的线程 ps -mp pid -o THREAD,tid,time | sort -rn

3.将需要的线程ID转换为16进制格式 printf "%x\n" tid

4.打印线程的堆栈信息 到了这一步具体看堆栈的日志来定位问题了 jstack pid |grep tid -A 30

Linux 从日志获取ip 取top10

cat *.log sort uniq -c head -n

现在一个进程在写入一个文件,比如log,如果现在直接rmrf会发生什么,文件会直接被删掉吗?

如果你直接运行 rm -rf 删除一个正在被写入的文件(比如日志文件),文件的目录项(即文件名的指针)会被删除,但文件本身不会立即从磁盘上消失。如果有进程仍然在使用该文件,操作系统会保持该文件的数据块直到所有对它的引用都被关闭为止。

CPU飙升的场景怎么排查?用哪些工具?

跟上面一样

Java

c++有析构函数可以自己回收,java有什么

我觉得是垃圾回收器,但是这人回答的是try with resource

其实是finalize()方法

要给一个类增加收尾(finalizer ),你只要定义finalize ( ) 方法即可。Java 回收该类的一个对象时,就会调用这个方法。在finalize ( )方法中,你要指定在一个对象被撤消前必须执行的操作。垃圾回收周期性地运行,检查对象不再被运行状态引用或间接地通过****其他****对象引用。就在对象被释放之前,Java 运行系统调用该对象的finalize( ) 方法。

Java里自己写一个String,会运行我们自己写的吗

自定义的java.lang.String无法加载进内存

基于JVM的双亲委派机制,类加载器收到了加载类的请求,会把这个请求委派给他的父类加载器。
而只有父类加载器自己无法完成加载请求时,子类才会自己加载。
这样用户自定义的String类的加载请求就会最终达到顶层的BootStrap ClassLoader启动类加载器,
启动类加载器加载的是系统中的String对象,而用户编写的java.lang.String不会被加载。

命名管道和匿名管道的区别

匿名管道一般用于父子进程之间,子进程复制父进程的文件表,找到文件描述符,就能找到管道,比如一个进程要做一些耗时操作,就可以fork子进程去做。命名管道,需要管道名,用open命令去打开,一般进程的话都能支持。

数据结构

红黑树是怎样的数据结构?有什么特点?

  1. 有序性:红黑树是一种有序的数据结构,对于树中的任意节点,其左子树上所有节点的键值都小于当前节点的键值,其右子树上所有节点的键值都大于当前节点的键值。
  2. 自平衡:红黑树在插入和删除操作中通过旋转和重新着色来保持树的平衡,使得树的高度大致保持在log(n),其中n是树中节点的数量
  3. 性能保证:红黑树提供了对插入、删除和查找操作的最优时间复杂度保证,即O(log n)

排序算法比较

对比

JVM

垃圾收集器回收不掉的怎么办

不知道

类加载过程

  1. 加载:在内存中生成一个代表该类的 Class 对象,作为方法区这些数据的访问入口
  2. 验证:这一阶段的目的是确保 Class 文件的字节流中包含的信息符合《Java 虚拟机规范》的全部约束要求
  3. 准备:准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这里所设置的初始值”通常情况”下是数据类型默认的零值
  4. 解析:解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,通过解析操作符号引用就可以直接转变为目标方法在类中方法表的位置,从而使得方法可以被调用
  5. 初始化阶段是执行初始化方法 <clinit> ()方法的过程,是类加载的最后一步

G1收集器的最终标记和CMS重新标记的区别

  • CMS重新标记: 暂停,采用增量更新,即标记并发期间增量的引用对象,但不会取消标记并发标记期间产生的垃圾,所以不能处理浮动垃圾,宁愿放到下次回收处理,也不愿错删
  • G1最终标记:需要暂停整个应用,

为什么要打破双亲委派模型

我们比较熟悉的 Tomcat 服务器为了能够优先加载 Web 应用目录下的类,然后再加载其他目录下的类,就自定义了类加载器 WebAppClassLoader 来打破双亲委托机制。

为什么呢?一个web容器可能要部署多个应用程序,不同的应用程序之间可能会依赖同一个第三方库的不同版本,不能要求同一个类库在同一个服务器只有一份,所以tomcat要具有隔离性,每个服务都是独立的。

ThreadLocal内存泄漏问题

ThreadLocalMap的Entry的Key是弱引用,如果外部没有强引用指向Key,Key就会被回收,而value由于Entry强引用指向了它,导致无法被回收,但是value又无法被访问,因此发生内存泄漏。 所以,开发者应该尽量在使用完毕后及时调用remove()删除节点。 如果开发者粗心忘记主动删除了,ThreadLocal也做了很多努力,在get()set()remove()时会自发的检查过期节点,并清理掉他们,最大程度的来避免程序发生「内存泄漏」,还是非常贴心的。

并发

轻量级和重量级怎么划分

不知道

创建十个线程,如何保证十个线程同时开始工作

使用倒计时锁(CountDownLatch): 倒计时锁允许一个或多个线程等待一组事件发生。你可以设置一个倒计时为10的锁,每个线程在开始跑步前都调用countDown()方法,当计数达到0时,所有线程都会同时开始执行。

协程是什么

协程是一种用户态的轻量级线程,其调度完全由用户程序控制,而不需要内核的参与。协程的切换开销非常小,因为只需要保存和恢复协程的上下文,而无需进行内核级的上下文切换。

守护线程是什么

守护线程(Daemon Thread)是Java中一种特殊的线程,主要用于执行一些辅助任务,如垃圾回收、缓存管理等。 与普通线程(非守护线程)相比,守护线程的特点是它会在所有非守护线程结束后自动关闭。 这意味着当应用程序中没有非守护线程在运行时,JVM会自动关闭所有守护线程。

Spring和SpringBoot

哪个组件负责HTTP的解析

DsipacherServlet和HandlerMapping

SpringBoot用了哪些设计模式

  • 工厂设计模式 : Spring使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
  • 代理设计模式 : Spring AOP 功能的实现。
  • 单例设计模式 : Spring 中的 Bean 默认都是单例的。
  • 适配器模式 :我认为HadnlerAdaptor是适配器模式

SpringBoot的启动流程

谁问谁sb

SpringBoot自动配置

梳理一下,以下是AutoConfigurationImportSelector的主要工作:

  • 扫描类路径: 在应用程序启动时, META-INF/spring.factories 文件,会查找所有实现了 AutoConfiguration 接口的类
  • 条件判断: 对于每一个发现的自动配置类,AutoConfigurationImportSelector 会使用条件判断机制(通常是通过 @ConditionalOnXxx注解)来确定是否满足导入条件。这些条件可以是配置属性、类是否存在、Bean是否存在等等。
  • 根据条件导入自动配置类: 满足条件的自动配置类将被导入到应用程序的上下文中。这意味着它们会被实例化并应用于应用程序的配置。

Springboot如何实现数据预热

ApplicationRunner接口的run方法

计算机网络

session和cookie的区别

  • 作用范围不同,Cookie 保存在客户端(浏览器),Session 保存在服务器端。
  • 存取方式的不同,Cookie 只能保存 ASCII,Session 可以存任意数据类型,一般情况下我们可以在 Session 中保持一些常用变量信息,比如说 UserId 等。
  • 有效期不同,Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,Session 一般失效时间较短,客户端关闭或者 Session 超时都会失效。
  • 隐私策略不同,Cookie 存储在客户端,比较容易遭到不法获取,早期有人将用户的登录名和密码存储在 Cookie 中导致信息被窃取;Session 存储在服务端,安全性相对 Cookie 要好一些。
  • 存储大小不同, 单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie。

epoll一定是最快的吗

不一定。epoll的一个缺点,当事件触发比较频繁时,回调函数也会被频繁触发,此时效率就未必比select 或 poll高了。所以epoll的最佳使用情景是:连接数量多,但活跃的连接数量少。

如果四次挥手中主动关闭方发送FIN后直接关闭连接,会有什么问题

(假设主动方是客户端被动方是服务器)这题的答案按面试官的解释,服务器会重传第三次挥手的FIN报文,但此时端口已经被回收,如果此时有新的连接想要使用这个端口,那么重传的FIN就会被新客户端误认为是服务器主动发送的FIN,出现意外情况。

在无丢包、时延大的通信环境下使用TCP和UDP区别

为什么有了TCP还要QUIC

  • 减少延迟- QUIC 通过将 TLS 握手与连接设置相结合来最大限度地减少延迟。这也称为0-RTT (零往返时间)。它可以更快地建立连接并提高 Web 应用程序的性能。
  • 多路复用- 通过多路复用,QUIC 可以通过单个通道发送多个数据流。它极大地帮助了下载多个文件(即图像、Javascript、CSS 等)的客户端应用程序。
  • 连接迁移- 通过连接 ID 来标记通信的两个端点,客户端和服务器可以各自选择一组 ID 来标记自己,因此即使移动设备的网络变化后,导致 IP 地址变化了,只要仍保有上下文信息(比如连接 ID、TLS 密钥等),就可以“无缝”地复用原连接,消除重连的成本,没有丝毫卡顿感,达到了连接迁移的功能
  • 安全性增强- QUIC 可在 TLS 1.3 上运行,可提供更好的安全性。此外,它还会加密协议的大部分内容,而 TCP 和 TLS 则仅加密 HTTP 负载。与 TCP 相比,QUIC 更能抵御安全攻击。
  • 广泛支持- 自推出以来,其采用率不断上升。这进一步增强了其有效性。

服务器有10w个长连接/即socket,应该用什么数据结构存

我的想法啊,哈希表 + 优先队列: 使用哈希表存储连接,同时用优先队列按时间排序以便进行超时处理。

手写题

单例模式

public class Singleton {
    private volatile static Singleton singleton;
    private Singleton(){}
    public static Singleton getInstance()
    {
        if(singleton==null)
        {
            synchronized (Singleton.class)
            {
                if(singleton==null)
                {
                    singleton=new Singleton();
                }
            }
        }
        return  singleton;
    }
}

为什么要加volatile?因为singleton=new Singleton();分三步

  • ①内存分配:为singleton对象分配一片内存
  • ②对象构造:调用构造函数构造一个singleton对象,存入已分配的内存区
  • ③地址绑定:将指针instance指向这片内存区(执行完这步instance才是非 nullptr)

如果不加,那么变成132,那么线程A执行到13,挂起,线程B判断Instance!=null,然后返回Instance,但是instance还没初始化

两个线程交替打印

public class PrintOddEven {

    public  static int count;
    private static final Object object=new Object();
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while(count<100)
                {
                    synchronized (object)
                    {
                        if(((count&1)==0))
                        {
                            System.out.println(Thread.currentThread().getName()+":"+count++);
                        }
                    }
                }
            }
        },"偶数").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while(count<100)
                {
                    synchronized (object)
                    {
                        if((count&1)!=0)
                        {
                            System.out.println(Thread.currentThread().getName()+":"+count++);
                        }
                    }
                }
            }
        },"技术").start();
    }
}

三个线程交替打印123

package org.example;

import sun.awt.windows.ThemeReader;
import sun.reflect.generics.tree.Tree;

public class PrintOddEven {

    public  static int count=1;
    private static final Object object=new Object();
    public static void main(String[] args) {
        new Thread(new Runnable(){
            @Override
            public  void run()
            {
                while(count<100)
                {
                    synchronized (object)
                    {
                        if(count%3==1)
                        {
                            System.out.println(Thread.currentThread().getName()+" "+1);
                            count++;
                        }
                    }
                }
            }
        },"一").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while(count<100)
                {
                    synchronized (object)
                    {
                        if(count%3==2)
                        {
                            System.out.println(Thread.currentThread().getName()+" "+2);
                            count++;
                        }
                    }
                }
            }
        },"二").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while(count<100)
                {
                    synchronized (object)
                    {
                        if(count%3==0)
                        {
                            System.out.println(Thread.currentThread().getName()+" "+3);
                            count++;
                        }
                    }
                }
            }
        },"三").start();
    }
}

智力题场景题

20个有序数组,每个数组500个元素,用最快的方法找出最大的第500个数及这个方法的时间复杂度

从20个数组中各取一个数,并记录每个数的来源数组,建立一个含20个元素的大根堆。此时堆顶就是最大的数,取出堆顶元素,并从堆顶元素的来源数组中取下一个数加入堆,再取最大值,一直这样进行500次即可。

49只老虎1只羊

A.每次只有一只老虎能吃掉一只羊,而这只老虎在吃了羊后就会变成一只羊。

B.所有的老虎都是完全理性的,生存是优先考虑的。

C.老虎可以吃草,但在可以安全吃羊的情况下一定会吃羊。

答案:

如果只有1只老虎(n = 1),它肯定会吃掉羊,因为它不需要担心被吃掉。

2 只老虎怎么样?因为这两只老虎都是完全理性的,任何一只老虎可能会思考如果它吃了羊会发生什么。任何一只老虎在想:如果我吃了羊,我就会变成一只羊;然后我将被另一只老虎吃掉。所以为了保证最高的生存可能性,任何一只老虎都不会吃掉羊。

如果有 3 只老虎,羊就会被吃掉,因为每只老虎都会意识到一旦它变成一只羊,就会剩下2只老虎,它不会被吃掉。所以第一个想到这一点的老虎就会吃掉羊。

如果有 4 只老虎,每只老虎都会明白,如果它吃了羊,就会变成一只羊,由于还有其他 3 只老虎,所以它将被吃掉。所以为了保证最大的生存可能性,没有老虎会吃羊。

按照同样的逻辑,我们自然可以证明,如果老虎的数量是偶数,那么羊不会被吃掉。 如果数字是奇数,羊会被吃掉。 对于本题 n = l00,羊不会被吃掉。

5和3升的杯子怎么求4升

先3升满,倒进5升,再3升满倒进5升,此时3升杯子剩余1升,接下来不用说了

10亿个数求topk

是否并发:并发可以显著提高运行速度,单机多核可以使用Hash将数据划分为多份子数据然后多线程并发排序,多机可以使用Hash+Socket方法将数据分发到多台机器上

是否有足够内存:如果机器内存足够可以直接在内存中使用Hash对数据进行切分,如果机器内存不足可以将原始文件切分成多个小文件

现在有A、B两组url,量级都是50亿(内存放不下),怎么找出它们间相同的数据

遍历文件a,对每个 url 根据某种hash规则,求取hash(url)/1024,然后根据所取得的值将 url 分别存储到1024个小文件,这样 url 就被hash到 1024 个不同级别的文件中。

分别比较文件,a0 VS b0,…… ,a1023 VS b1023,求每对小文件中相同的url时:把其中一个小文件的 url 存储到 hashmap 中,然后遍历另一个小文件的每个url,看其是否在刚才构建的 hashmap 中,如果是,那么就是共同的url,存到文件中。

把1024个文件中的相同 url 合并起来

有10w个字符串,三种匹配模式,一种是固定字符串,一种有可变的uid,一种有**号,怎么快速判断字符串是否在集合里

预处理

  • 将固定字符串存储在哈希集合中。
  • 将带有可变UID的字符串根据固定部分存储在前缀树或哈希映射中。
  • 将带有通配符**的模式转换为正则表达式并存储。

查询阶段

  • 优先在哈希集合中查找固定字符串,查询时间为 O(1)。
  • 如果模式中含有可变UID,则基于前缀树或哈希映射查找固定部分,匹配成功后再验证 UID。
  • 如果字符串中可能匹配通配符模式,则通过遍历所有通配符模式并使用正则表达式匹配。

设计一个微博的评论系统,要求展示热点评论,要求处理高并发访问场景。如何设计数据库表,如何保证高并发访问时微博不会挂掉。

服务拆分:

  1. 评论和微博虽然关联性很强,但功能涉及属于主体微博的附属品,故而单独拆分评论服务便于演进;同时评论和发微博不一样,评论可以通过排队来解决
  2. 其次,评论微博及看微博评论,也推荐拆分服务,两者的压力完全不一样,拆分更利于扩展

负载均衡算法选择:虽然评论微博跟指定微博有关,但考虑到评论时不会使用缓存,故而将请求发送给任意服务器都可以,这里选择“轮询”或者“随机”算法

如果你的舍友在下载东西,速度很快,但你的网络很卡,这和你舍友有关系吗

宽带有一个固定的带宽上限(例如 100 Mbps 或 200 Mbps),所有连接到这个网络的设备共享这个带宽。如果你的舍友在下载大量数据,可能会占用大部分带宽,导致其他设备的网速变慢

现在有个场景,比如说pdd助力,你砍我我砍你,我们要设计俩接口和数据库表,接口一可以查找我们之间是否存在砍or被砍的关系,接口二可以查找我砍了谁or谁砍了我,如何合理设计数据库表呢

我觉得就两个表吧,一个详细记录谁砍了谁

另一个只记录有砍行为的关系表

有一台16G内存的电脑,要保存2的32次方个qq号的状态,怎么设计,大概占多少内存空间。不能用数据库等中间件

位图(Bit Array):如果要存储每个 QQ 号的状态,可以使用位图,其中每一位代表一个 QQ 号的状态。假设每个 QQ 号用一个位表示状态,则

内存=2的32次方/8 字节=4GB

代码中怎么实现超时控制,一个请求,怎么把他限制在固定的时间里

总体来说就是,在提交服务端的查询请求时,会开启定时任务,检查超时。如果定时任务到期,还未收到结果则会触发超时通知。如果客户端还未成功发送数据,则认为是客户端自己超时。如果已经将数据发送出去,则认为是服务端超时。这相当于是一个看门狗的形式处理了,就是说,不管服务端和客户端本身如何,总能被这东西给发现,所以这种超时控制是精确的

同一时刻要更新大量数据,怎么办

应该就是分两个字段去存,到点换着读,用redis hash key是商家id,field一个pre一个now,value价格,业务里面根据时间切换读哪个


  目录