Java 面试宝典

一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅知识面试题,也是你 Java 知识点的扫盲贴。

  • 回答在进行分库分表后,如果我们对业务的预估数据不准确,由于数据增长超出预期导致表再次不够用了,那么这时应该怎么办呢?遇到这种情况我们首先需要考虑的是能否利用一些常规手段,如历史数据归档或者清理不必要的数据,通过这样的常规手段来减少表的数据量,可以参考这篇文章:如果常规的方式不行,那就只能进行二次分表了。二次分表我们主流的做法是选择水平双倍扩容法,即在原来的基础上扩大一倍,例如原来表为64张,此次扩容则是128张。为什么选择双倍呢?可以参考这篇:分库分表时为什么数量都推荐使用2的幂次方?扩容简单,但是扩容后的工程量就比较大了,我们需要更新分表算法、进行数据迁移,在数据迁移的过程中我们还需要考虑考虑到业务的连续性,最关键的是如何做到无损、无缝、平滑的迁移。数据迁移可以参考这篇文章:单库系统如何迁移到分库分表系统?所以,为了避免二次分表,我们还是需要尽可能地避免这种情况发生,在决定分库分表的时候

    2024-08-04
  • 回答在我们分库分表时,我们首先需要确认我们要划分为多少个库,每个库多少张表,这个时候我们一般都是建议使用2^N,也就是2、4、8、18、32这样的数据。这样做有什么好处呢?一、哈希算法的优化如果我们的分库分表策略采用的是Hash取模策略时,若分片的数量是2^N,则哈希值对分片数量取模的计算可以通过简单的位与运算来完成。例如,假设分片数量是8(即2^3),要计算哈希值H对8取模,可以用位与运算H&(8-1)来替代H%8。位与运算(&)比取模运算(%)更高效,因为它直接操作二进制位而无需进行除法计算。二、数据分布更加均匀在分裤时,如果我们库、表的分片数量都是2^N,例如8库64表,这样我们就可以将64张表均匀分布到8个库中,每个库8张表。同时,在加上数据的分片策略采取Hash取模策略,这样就会使得数据可以更加均匀地分布到各个分片中。三、更利于扩容当我们在做分库分表时,肯定要考虑

    2024-08-04
  • 回答在分库分表后,我们的数据都分散不同数据库的不同表中,那遇到后端进行模糊查询怎么办?比如用户分库分表后,我们需要查询所有姓“张”的用户,怎么办?你在网上会看到各种各样的奇淫技巧,其实,真正可行的方案只有一个,那就是上搜索引擎,比如ES。该方案比任何奇淫技巧都有效,尤其是面对海量数据和多维度的复杂查询。你数据库优化得再牛逼,索引再屌,在海量数据面前,执行类似like'%大明哥%'这样的操作都顶不住。所以,方案只有一个,上搜索引擎。

    2024-08-04
  • 回答当我们完成数据迁移且数据校验没什么问题的时候,我们就开始切流了。为什么是切流,而不是一把切呢?因为我们无法保证新系统就一定是没有问题的,如果有问题的话,直接上线新系统关闭老系统这样的操作会带来史诗级灾难。所以,为了避免你年底背绩效,你需要灰度、按流慢慢来切换到新系统。第一步:切流测试如果线上有流量录制功能,我们可以在线上采集一些流量,然后将新库数据dump到准生产环境(或者独立的测试环境),进行回放。回放完成后,我们需要进行数据验证,待数据验证无误后,准备发布上线。第二步:二次校验条件允许的情况下,为了安全起见,在正式切流之前,我们可以进行二次校验,这次校验最好是全量,如果二次校验发现还有数据差异,则需要排查下问题,同时延后切流。第三步:开启双写切流在系统低流量阶段(一般都是凌晨),找准时机,放入很小一部分写流量到新库,比如10笔交易,观察日志,数据校验。在双写切流时,可以暂时关闭bi

    2024-08-04
  • 回答要分为两种情况,停机和不停机。停机迁移如果可以进行停机迁移,那么就提前几天发布一个公告,通知用户会预计停机多长时间。到了预订时间就关闭应用进行数据迁移,数据完成后进行数据对比,数据对比没有问题就发布新系统。停机迁移是一个非常理想的情况,可以避免很多麻烦,当然,在绝大多数情况下我们是不会允许停机迁移的,毕竟数据迁移是一个大工程,不是几个小时能够处理好的。大明哥经历过一次数据迁移,前前后后搞了两个多月。不停机迁移如果需要不停服将单库数据迁移到分库分表系统去的话,我们需要一个数据迁移系统,这个数据迁移系统的职责就是负责将数据从单库按照某种路由规则迁移到分库分表的环境中。如下:那数据迁移程序怎么做呢?每次从单库单表中查询一批数据(比如每批5000条),然后按照路由规则写入到分库分表中。但是这样做会有一个小问题,那就是如果数据迁移程序中途蹦了怎么办?这时你不知道已经同步到哪里了,难道又要重头开始

    2024-08-04
  • 回答当MySQL中的单表数据量很大以后(超千万级别),我们一般都会考虑采用分库分表,主要因为单表单库的性能瓶颈和存储限制。详情见:我们为什么需要分库分表?为什么MySQL中单表操作千万后就要分库分表?但是,当数据量一大就要分库分表吗?其实不然,虽然分库分表在一定程度上提升了系统的性能和扩展了存储限制,但是分库分表的实现方案比较复杂,需要考虑的问题有很多,比如如何从单库迁移系统到分库分表系统,如何进行数据迁移,而且分库分表后也会带来很多问题,比如事务、跨库join等等。分库分表后,如何解决分页查询问题?分库分表后,如何解决join的问题?分库分表后,有哪些查询问题?该如何解决?所以,不到万不得已的时候,大明哥不建议一上来就分库分表!!如果不分库分表,那又有什么方案来解决这种单表数据量过大的问题呢?我们可以考虑如下几种方案:优化查询和索引如果是查询问题,我们首先需要考虑的是是否可以通过优化查询

    2024-08-04
  • 回答分库分表后,我们通常会遇到下面几个查询问题:跨库查询问题分页查询问题排序问题聚合问题关联查询问题为了更好地理解,大明哥按照分库分表的维度来对这几个问题进行阐述。垂直分库产生的问题垂直分裤一般来说都是按照业务维护来进行拆分,其核心理念就是专库专用,即将不同的业务数据分别放入到不同的数据库中。将原来单一的数据库按照业务维度拆分为多个库,虽然在性能上能够带来很大的提升,但是带来的问题也是非常多,主要设计如下几个问题:跨库join问题将不同的业务数据拆分到不同的库中,这就会导致原来一个join就能解决的问题现在解决不了。那怎么办呢?有如下几种方案:数据冗余:将需要jion查询的数据表字段冗余在一个库中,这样就避免了跨库join查询了。应用层join:将原来的join操作拆分为多个单表查询,然后在应用层执行join操作。全局表:将需要经常进行join操作的小表设计成全局表,然后存储在对应的库中,

    2024-08-04
  • 回答在进行水平分库分表后,我们可能会将一个类型的数据拆分到了多个库中的多个表中去了,比如对于订单表t_order,为了用户端能够更加方便地查询他的订单,我们一般都会选择用户id(uid)作为分库分表的键。虽然它方便了用户端查询,但是他给商户端和运营端带来了麻烦(不考虑其他架构的情况下),比如查询分页如何做?假如我们要执行下面这条SQL语句:select*fromt_orderorderbyidasclimit10,5;//查询第三页的5条数据为了更好地演示和理解,我们只将t_order表拆分为两个表:t_order_1、t_order_2,各自数据如下:t_order_1t_order_2180840758976913408018084076026666188801808407595582439424180840760864350208018084076237136363521808407

    2024-08-04
  • 详情在拆分之前,我们系统中很多列表和详情所需要的数据大部分从多张表中获取的,这个时候我们使用join操作即可。但是分库分表后,这些表可能会被分散在多个库中,这个时候我们是无法进行join操作的。虽然ShardingSphere支持跨库join,但是我们基于架构规范、安全、性能方面的考虑,我们都会强制禁止跨库join操作的。那怎么解决呢?有如下几种方案:一、全局表OR数据冗余为了避免join操作我们可以使用全局表,即一个表中存储所有需要查询的字段,这样我们就只需要查询这一张全局表了,而不需要执行join操作。比如在订单列表中,我们总是会显示用户名,这时我们可以在t_order表中冗余用户信息:CREATETABLEt_order(order_idINTPRIMARYKEY,user_idINT,user_nameVARCHAR(100),--冗余的用户信息product_idINT);这样在

    2024-08-04
  • 回答在分库分表中,我们大概率都会选择表的id来作为拆分的键,例如我们有一张tb_user表,它有四个字段:id、name、phone、address,我们利用tb_user的id作为拆分id。在大多数的场景下,我们都是利用id来查询tb_user表中的数据,这种情况是没有问题的。但是,有些场景我们需要根据其他不是拆分键的id来查询,例如查询name='张三',如下:select*fromtb_userwherename='张三'由于name不是拆分键,所以我们无法定位到具体的某张表上去执行这条语句,于是就会对所有的tb_user表都执行一次:对于这种情况,如果有100张表,就要查询100次,当然你可以使用union操作合并查询,但还是避免不了要查询100张表。所以,针对这种随着表越来越多,查询的表就会越来越多,这就说所谓的读扩散。那怎么解决这种情况呢?目前主流的方案有三种:建索引表引入ES

    2024-08-04
  • 回答目前主流的分表策略有三种:Range拆分Hash取模拆分Rang拆分和Hash取模拆分的组合Range拆分Range拆分也叫做范围分表,即根据某个字段的范围来分表,比如我们将订单表的主键order_id,按照0~500万,500~1000万的梯度来分表,如下:有时候我们也会根据时间来排序,比把按年维度拆分工单表:这种拆分的优点是它有利于扩容,比如我们按照时间来拆分,理论上是可以无限的扩容,而且不需要担心已经拆分好的数据。但是它可能会有热点问题,比如2024年的工单表,在2024年内,几乎所有的请求都会打在2024年这张分表中,而2022、2023的表几乎是已半归档的方式存在。同时,按照时间来拆分的话,也可能会出现数据倾斜的问题,比如随着业务的扩展,2024年的工单数据肯定比2023的数据多得多。Hash取模拆分选定一个Hash算法对某个字段(例如user_id、order_id、ord

    2024-08-04
  • 回答主要是因为MySQL使用B+树作为索引结构,当数据量较小时,B+树的的高度较低,查询时需要访问的节点较少,性能也就高些。当数据量增加到千万级别,B+树的高度就会增加,树的高度增加会导致每次查询需要访问更多的磁盘页,增加了磁盘I/O操作次数,导致查询性能下降。扩展MySQL的B+树高度计算Mysql的默认存储引擎是InnoDB,InnoDB将表的数据存储在数据页中,每个数据页的默认大小是16KB。B+树的叶子节点存的是数据,非叶子节点存的是键值+指针,索引组织表通过非叶子节点的二分查找法以及指针确定数据在哪个页中,进而再去数据页中查找所需的数据。如下:假设我们有一张表,其主键类型为bigint(长度为8字节),在InnoDB中,指针的大小为16字节,所以非叶子节点能够存储的数据个数为16KB/14B=1170,所以2层的话就能代表1170*1170个叶子页。加入我们一行数据为1KB,那么

    2024-08-04
  • 回答衡量是否要分库分表的关键指标是数据量是数据量和业务模块。什么时候分库?分库主要是为了解决单个数据库实例存储容量和并发处理能力的瓶颈问题。当系统的并发量过大时,数据库就会成为我们系统的瓶颈,主要是因为数据库的连接是有限的,当我们数据库的读写请求过高,导致数据库的连接数不足时,这个时候我们就需要考虑分库了。通过增加数据库实例的方式来提供更多的数据库连接,从而提高系统的性能。同时,随着我们业务系统的逐渐庞大,模块之间的耦合度越来越高,越来越复杂的时候,我们可以通过分库的方式将不同的业务模块分散到不同的数据库中,实现业务解耦。什么时候分表?分表主要是为了解决单张表数据量过大导致的查询和写入性能问题。当我们某张表的数据量过大时,即使系统的并发度不高,也会因为数据库过大而导致新增、查询的性能降低,当然,这个时候我们可以做一些优化措施,比如加索引之类的,如果能优化,也许就不需要分表了。但是,如果我们

    2024-08-04
  • 回答为什么需要分库分表?一个原因:单表、单库已经无法支撑我们的业务了。一、数据库容量限制存储容量:单个数据库的存储容量是有限的,当我们应用的数据量达到一定规模时,但是数据库就无法存储整个应用的所有数据。IO性能:当数据库的容量很大时,磁盘的读写性能会成为瓶颈,影响查询和写入的效率。二、并发处理能力限制CPU和内存限制:单库的CPU和内存资源有限,他是无法应对高并发场景的连接数限制:数据的最大连接数是有限的,如果在高并发场景下,容易造成连接数耗尽,连接数耗尽,导致数据库性能下降或者连接失败。在MySQL中我们可以通过以下命令获取最大连接数:showvariableslike'%max_connections%'所以,分库:主要是为了解决单个数据库实例存储容量和并发处理能力的瓶颈问题。分表:主要是为了解决单张表数据量过大导致的查询和写入性能问题。所以,无论是分库还是分表,最终目标都是为了性能问

    2024-08-04