建议收藏】小滴课堂后端面试题(9)~持续更新中

994 阅读3分钟

一、数据库-单表1千万数据,未来1年还会增⻓多500万,性能⽐较慢,说下你的优化思路

答案:

思路

- 千万不要⼀上来就说分库分表,这个是最忌讳的事项

- ⼀定要根据实际情况分析,两个⻆度思考

  • 不分库分表

    • 软优化
      • 数据库参数调优
      • 分析慢查询SQL语句,分析执⾏计划,进⾏sql改写

        和程序改写

      • 优化数据库索引结构
      • 优化数据表结构优化
      • 引⼊NOSQL和程序架构调整
    • 硬优化
      • 提升系统硬件(更快的IO、更多的内存):带宽、

        CPU、硬盘

  • 分库分表

    • 根据业务情况⽽定,选择合适的分库分表策略(没有通

      ⽤的策略)

      • 外卖、物流、电商领域
    • 先看只分表是否满⾜业务的需求和未来增⻓
      • 数据库分表能够解决单表数据量很⼤的时,数据查询

        的效率问题,

      • ⽆法给数据库的并发操作带来效率上的提⾼,分表

        的实质还是在⼀个数据库上进⾏的操作,受数据库

        IO性能的限制

    • 如果单分表满⾜不了需求,再分库分表⼀起

结论

- 在数据量及访问压⼒不是特别⼤的情况,⾸先考虑缓存、读

- 写分离、索引技术等⽅案如果数据量极⼤,且业务持续增⻓快,再考虑分库分表⽅案

二、描述Kafka消息队列Topc、Partition、副本、Group的概念和作⽤

答案:

Topic

  • 每条发布到Kafka集群的消息都有⼀个类别,这个类别被称为

    Topic,主题的意思

Partition分区

  • kafka数据存储的基本单元,topic中的数据分割为⼀个或多个

    partition,每个topic⾄少有⼀个partition,是有序的

  • ⼀个Topic的多个partitions, 被分布在kafka集群中的多个

    server上

  • 消费者数量 <=⼩于或者等于Partition数量

Replication 副本(备胎)

  • 同个Partition会有多个副本replication ,多个副本的数据是

    ⼀样的,当其他broker挂掉后,系统可以主动⽤副本提供服

  • 默认每个topic的副本都是1(默认是没有副本,节省资源),

    也可以在创建topic的时候指定

  • 如果当前kafka集群只有3个broker节点,则replication

    factor最⼤就是3了,如果创建副本为4,则会报错

基于消费者组可以实现:

  • 基于队列的模型:所有消费者都在同⼀消费者组⾥,每条消

    息只会被⼀个消费者处理

  • 基于发布订阅模型:消费者属于不同的消费者组,假如每个

    消费者都有⾃⼰的消费者组,这样kafka消息就能⼴播到所有

    消费者实例上

三、⾼并发Java开发⾥⾯线程池-ThreadPoolTaskExecutor 和ThreadPoolExecutor的区别

答案:

线程池种类

  • ThreadPoolExecutor,这个类是JDK中的线程池类,继承⾃

    Executor,⾥⾯有⼀个execute()⽅法,⽤来执⾏线程,线程

    池主要提供⼀个线程队列,队列中保存着所有等待状态的线

    程,避免了创建与销毁的额外开销

  • ThreadPoolTaskExecutor,是spring包下的,是Spring为我

    们提供的线程池类 Spring异步线程池的接⼝类是

    TaskExecutor,本质还是 java.util.concurrent.Executor

开发的时候spring会先搜索TaskExecutor类型的bean或者名字为 taskExecutor的Executor类型的bean,*

所以我们最好来⾃定义⼀个线程池,加⼊Spring IOC容器⾥⾯,即可覆盖现有的进⾏调优


@EnableAsyncpublic class ThreadPoolTaskConfig {

@Bean("threadPoolTaskExecutor")

public ThreadPoolTaskExecutor

threadPoolTaskExecutor(){

ThreadPoolTaskExecutor

threadPoolTaskExecutor = new

ThreadPoolTaskExecutor();

//线程池创建的核⼼线程数,线程池维护线程的最少数量,

即使没有任务需要执⾏,也会⼀直存活

//如果设置allowCoreThreadTimeout=true(默认

false)时,核⼼线程会超时关闭

threadPoolTaskExecutor.setCorePoolSize(4);

//最⼤线程池数量,当线程数>=corePoolSize,且任务队列

已满时。线程池会创建新线程来处理任务

//当线程数=maxPoolSize,且任务队列已满时,线程池会拒

绝处理任务⽽抛出异常

threadPoolTaskExecutor.setMaxPoolSize(8);

//缓存队列(阻塞队列)当核⼼线程数达到最⼤时,新任务会

放在队列中排队等待执⾏

threadPoolTaskExecutor.setQueueCapacity(124);

//当线程空闲时间达到keepAliveTime时,线程会退出,直

到线程数量=corePoolSize //允许线程空闲时间60秒,当maxPoolSize的线程在空闲

时间到达的时候销毁

//如果allowCoreThreadTimeout=true,则会直到线程

数量=0

threadPoolTaskExecutor.setKeepAliveSeconds(30);

//spring 提供的 ThreadPoolTaskExecutor 线程

池,是有setThreadNamePrefix() ⽅法的。

//jdk 提供的ThreadPoolExecutor 线程池是没有

setThreadNamePrefix() ⽅法的

threadPoolTaskExecutor.setThreadNamePrefix("⼩滴课堂

Spring⾃带Async前缀:");

threadPoolTaskExecutor.setWaitForTasksToCompleteOnSh

utdown(true);

// rejection-policy:当pool已经达到max size的时候,如何

处理新任务

// CallerRunsPolicy():交由调⽤⽅线程运⾏,⽐如 main 线

程;如果添加到线程池失败,那么主线程会⾃⼰去执⾏该任务,不会等

待线程池中的线程去执⾏

//AbortPolicy():该策略是线程池的默认策略,如果线程池队列满

了丢掉这个任务并且抛出RejectedExecutionException异常。

//DiscardPolicy():如果线程池队列满了,会直接丢掉这个任务并

且不会有任何异常//DiscardOldestPolicy():丢弃队列中最⽼的任务,队列满了,

会将最早进⼊队列的任务删掉腾出空间,再尝试加⼊队列

threadPoolTaskExecutor.setRejectedExecutionHandler(n

ew ThreadPoolExecutor.CallerRunsPolicy());

threadPoolTaskExecutor.initialize();

return threadPoolTaskExecutor;

}

}

//使⽤实战, 启动类可以不加@EnableAsync,改上⾯加

@Async("threadPoolTaskExecutor")