Bootstrap

【今日面经】Java后端面经(主要问Mysql跟Redis)

1、Redis缓存与内存的关系

Redis缓存与内存虽然都涉及数据的存储和访问,但在计算机系统和应用架构的上下文中,它们的区别体现在以下几个方面:

  1. 角色与位置

    • 内存:内存是计算机硬件系统中的一个组成部分,它是CPU可以直接访问的数据存储区域,用于暂时存放运行中的程序和数据。内存是计算资源的重要一环,任何进程或程序都需要通过内存来执行和处理数据。

    • Redis缓存:Redis是一个独立的、基于内存的数据存储系统,它可以被部署为一个服务或进程,为应用提供缓存功能。Redis缓存是位于应用层面上的一种软件解决方案,它利用内存作为数据的存储介质,目的是为了加速数据访问速度,减轻数据库负担。

  2. 数据持久性

    • 内存:内存的数据在计算机断电或重启后会丢失,除非有额外的机制如虚拟内存交换文件、数据库缓存等将数据写入磁盘。

    • Redis缓存:Redis默认情况下将数据存储在内存中,但提供了多种数据持久化策略,如RDB快照和AOF日志,可以将内存中的数据定期或实时同步到磁盘,从而在服务器重启后恢复缓存数据。

  3. 数据管理与共享

    • 内存:操作系统管理和分配内存,不同的进程可以通过内存分配机制获得各自的内存空间,但各自的数据不直接共享。

    • Redis缓存:Redis作为一个服务,可以由多个进程或应用程序同时访问和共享数据。它通过客户端-服务器模型,允许跨进程、跨机器甚至是跨网络共享和访问同一份缓存数据。

  4. 数据结构与API

    • 内存:内存只是底层的硬件资源,不具备高级的数据结构或API接口,程序员需要通过编程语言提供的数据结构和内存管理函数来操作内存中的数据。

    • Redis缓存:Redis提供了丰富且高效的数据结构,如字符串、列表、集合、有序集合、哈希表等,并提供了丰富的命令接口(如GET、SET、LPUSH、HSET等),使得数据操作和管理变得更加简便。

  5. 缓存策略

    • 内存:内存的使用策略是由应用程序自身决定,可能涉及到内存分配、缓存替换算法等,但这些都是应用程序层面的设计和实现。

    • Redis缓存:Redis内置了多种缓存淘汰策略,如LRU、LFU、volatile-lru等,可以根据实际需求选择合适的策略来管理有限的内存资源,确保高价值数据得以保留。

2、如果不用Redis做缓存,把更多资源放在其它地方,可行嘛?

当然可行。尽管Redis因其高性能和丰富的数据结构在缓存场景中非常受欢迎,但这并不意味着它是唯一的选择。实际上,在某些场景下,根据业务需求和技术栈特点,可以选择其他的缓存解决方案,或者调整资源分配策略,将更多的资源投入到其他方面。

  1. 其他缓存技术

    • Memcached:另一种流行的内存键值存储系统,专注于简单的KV缓存,适合纯缓存场景,不支持Redis那样的复杂数据结构和持久化。
    • 本地缓存:如Java中的HashMap.NET中的MemoryCache等,将数据缓存在应用程序本地内存中,适合单机应用且数据量较小的场景。
    • 云服务商提供的缓存服务:如AWS的ElastiCache、Azure的Managed Cache Service等,它们基于Redis或Memcached,但提供了云环境下的托管服务。
  2. 数据库优化

    • 数据库自身的缓存机制:如MySQL的InnoDB Buffer Pool、PostgreSQL的shared_buffers等,通过优化数据库配置,提高数据库内部缓存效果,减轻I/O压力。
    • 索引优化:合理设计数据库索引,提高查询效率,一定程度上替代缓存的效果。
  3. 架构优化

    • 服务端渲染:对于Web应用,可以考虑将部分数据处理逻辑移到服务端,减少客户端对数据的频繁请求,从而降低对缓存的依赖。
    • 静态化处理:对于部分不变或极少变化的内容,可以预先生成静态页面或数据,存储在CDN中,以加快访问速度。
  4. 扩容硬件资源

    • 增加内存:如果资源允许,可以增大服务器内存,提高数据库或其他缓存系统在内存中的数据存储量。
    • SSD硬盘:使用固态硬盘(SSD)代替传统的机械硬盘,可大幅度提升数据读写速度,尤其对于热点数据,可通过智能缓存策略使其在SSD缓存中快速访问。

总之,是否选用Redis或者其他缓存方案,需要综合考虑业务需求、性能要求、成本预算、技术栈等因素。有时候,将资源投入到数据库优化、架构调整等方面同样可以获得性能提升,甚至在某些特定场景下比单纯增加缓存更有优势。

3、本地内存是什么?

本地内存(Local Memory)这个概念在不同的上下文中可能有不同的含义:

  1. 在计算机硬件层面,特别是讨论多线程编程时,本地内存通常指每个处理器核心(或每个线程)所拥有的私有内存空间,如线程栈内存。线程栈用于存储函数调用栈帧、局部变量和临时数据,这部分内存是每个线程独立管理的,不与其他线程共享。

  2. 在手机或移动设备领域,本地内存常常指的是手机内部的存储空间,即非易失性存储器(如ROM),用于存储操作系统、应用程序、用户数据等。有时也会提到RAM(随机存取存储器),即手机的运行内存,这是操作系统和应用程序在运行过程中使用的内存,用于存放临时数据和加载执行的代码。

  3. 在Web开发或应用程序开发中,本地内存又可以指代应用程序在本地计算机上的内存,包括进程工作集(Process Working Set),也就是操作系统分配给应用程序实际占用的内存空间,包括代码、数据、堆栈以及其他辅助数据结构。

  4. 在HTML5 Web Storage API的范畴里,本地存储(Local Storage)是一种浏览器提供的在客户端本地保存数据的功能,不同于服务器端的存储,它可以持久地在用户的硬盘上保存一定量的数据,即使浏览器关闭也能保留这些数据。

综上所述,本地内存的概念随着具体的技术环境和语境会有不同的指向,但主要都围绕着“在某个计算实体(如线程、进程、设备)上,用于存储数据的私有或专用内存资源”。

4、Java中除了Redis有缓存概念还有在哪里

在Java中,除了Redis作为分布式缓存解决方案外,缓存概念也体现在很多其他场合:

  1. 本地内存缓存

    • 在Java应用内部,可以通过使用如java.util.concurrent.ConcurrentHashMapjava.util.Map等数据结构,在本地内存中实现缓存。例如,Spring框架提供的org.springframework.cache.Cache接口及其众多实现,如ConcurrentMapCache,可以在本地内存中存储和检索数据。
  2. 第三方缓存库

    • Guava Cache:Google Guava库提供了一个强大的本地缓存实现,支持自动加载、过期策略、大小限制等功能。
    • Caffeine Cache:一个高性能的本地缓存库,设计上优于Guava Cache,也支持丰富的缓存配置选项,已在Spring Boot等项目中广泛应用。
  3. ORM框架中的缓存

    • Hibernate二级缓存:Hibernate ORM框架支持二级缓存,可以在多个事务之间共享数据,减少对数据库的访问。可以配置为使用EHCache、Infinispan、 Hazelcast等作为二级缓存实现。
  4. JVM本地缓存

    • JVM本地内存:在单个Java应用内部,开发者可以通过定义全局静态变量、ThreadLocal等方式在JVM本地内存中实现简单的缓存。
  5. 框架内置缓存

    • Spring MVC的HttpSession缓存:在Web应用中,可以利用HttpSession来缓存用户会话相关的数据。
    • Spring Boot Cache abstraction:Spring Boot提供了对各种缓存技术(包括Redis、EHCache、Caffeine等)的统一抽象,方便开发者在不同场景下使用缓存。
  6. 数据库缓存

    • 数据库内建缓存:如MySQL的InnoDB Buffer Pool,Oracle的Buffer Cache等,它们存储最近访问过的数据块,减少对磁盘I/O的依赖。

总之,Java中的缓存不仅局限于Redis这样的分布式缓存系统,还包括各种本地内存缓存机制以及框架内建的缓存功能。

5、B+树索引与哈希索引的区别?为什么Mysql 用B+树索引

B+树索引和哈希索引是两种不同的数据结构,它们在索引的组织方式、查询效率、适用场景等方面有所不同:

  1. B+树索引

    • B+树是一种平衡多路搜索树,其特点是叶子节点包含所有的键值并对关键字进行排序,且叶子节点之间用链表相连,形成有序链表。非叶子节点仅存储键值和指向子节点的指针,不存储数据。
    • 查询效率:B+树索引支持范围查询和顺序扫描,查询效率较高且稳定,因为数据分布在多个层级的节点中,可以有效地减少磁盘I/O。
    • 插入和删除:插入和删除操作会导致树结构调整,但B+树的设计使其具有较好的自平衡能力,有利于保持较低的查询开销。
  2. 哈希索引

    • 哈希索引基于哈希表实现,通过哈希函数将键转化为哈希码,然后通过哈希码定位数据。理想情况下,查找时间为O(1)。
    • 查询效率:哈希索引对于点查询(精确匹配)非常高效,但不支持范围查询和排序操作,因为哈希表本质上是无序的。
    • 插入和删除:插入和删除操作较为简单,只需对哈希表进行相应更新即可,但可能出现哈希碰撞导致性能下降。

MySQL选择B+树索引的原因:

  • 范围查询和排序:B+树的叶子节点形成了一个有序链表,特别适合于范围查询和ORDER BY、GROUP BY等操作,而哈希索引无法有效支持此类操作。
  • 磁盘I/O效率:在磁盘存储的大数据场景下,B+树的多级索引结构能够减少磁盘I/O次数,提高查询性能。相比于哈希索引,B+树索引在数据分布均匀的情况下,即便遇到哈希碰撞,也不会导致性能明显下滑。
  • 页分裂问题较少:B+树在插入新节点时,即使出现满页,也只会导致页分裂,而非大量的哈希表重建。因此,在数据更新频繁的场景下,B+树表现更为稳定和高效。

因此,MySQL在大多数情况下采用B+树索引作为主要的索引结构,以适应复杂的SQL查询和大规模数据存储的需要。当然,MySQL InnoDB引擎也支持哈希索引,但在某些特定条件下(如InnoDB内存表或全文索引)会使用哈希索引以提升特定类型查询的速度。

6、说说分布式事务

分布式事务是指跨越多个数据库、服务或系统的一系列操作,这些操作作为一个整体,需要满足事务的ACID(原子性、一致性、隔离性、持久性)特性,即所有操作要么全部成功,要么全部失败。在分布式系统中,由于数据分布在多个节点,传统的事务处理机制无法直接适用,因此需要特殊的机制来保证分布式环境下的事务完整性。

分布式事务的常见解决方案和协议有:

  1. 两阶段提交(2PC, Two-Phase Commit)

    • 第一阶段(准备阶段):协调者询问所有参与者是否准备好提交事务,参与者根据自身情况回复yes/no/unknown。
    • 第二阶段(提交阶段):如果所有参与者都回复yes,则协调者指示所有参与者提交事务;如果有任何一个参与者回复no或未知,则协调者指示所有参与者回滚事务。
  2. 三阶段提交(3PC, Three-Phase Commit)

    • 相比2PC增加了预提交阶段,减少了阻塞时间,但仍然存在单点故障问题。
  3. TCC(Try-Confirm-Cancel)

    • Try阶段:尝试执行业务操作预留资源,不实际提交。
    • Confirm阶段:如果所有事务try成功,执行确认操作真正提交事务。
    • Cancel阶段:如果有任一事务try失败,则执行取消操作释放预留资源。
  4. Saga模式

    • Saga是一系列长事务操作的有序序列,每个操作是一个本地事务,每个操作完成后发布一个补偿操作(或逆向操作),在某一步失败时,通过回滚前面已完成步骤的补偿操作来恢复一致性。
  5. 分布式事务中间件

    • 通过引入中间件如Seata、DTM等,提供透明化的分布式事务解决方案,它们通常基于2PC、3PC、TCC或其他优化协议实现。
  6. 最终一致性

    • 对于一些对实时性要求不是特别严格的场景,可以采用最终一致性模型,通过消息队列、事件溯源等方式,保证在一段时间后,系统能够达到数据一致状态。

每种分布式事务解决方案都有其适用场景和局限性,实际应用中需要根据业务需求和系统复杂度选择合适的方法。

7、说说事务,以及ACID,它们之间有何联系

事务是数据库系统中一组操作的逻辑单位,这一组操作要么全部成功执行,要么全部不执行,确保了数据库状态的完整性和一致性。事务具有以下四个关键性质,被称为ACID特性:

  1. 原子性(Atomicity)
    事务中的所有操作被视为一个不可分割的单元,事务中的所有操作要么全部完成,要么全部不执行。如果事务在执行过程中发生任何错误,那么所有已完成的操作都将被撤销,确保数据库处于一个一致的状态。

  2. 一致性(Consistency)
    事务在执行前后,数据库都必须处于一致性状态,这意味着事务的执行结果必须符合数据库的完整性约束。例如,转账操作后,两个账户的总金额保持不变。

  3. 隔离性(Isolation)
    即使在并发执行多个事务的环境中,每个事务都应该像是在完全独立的数据库上执行一样,互不影响。为了避免并发事务之间的影响,数据库系统采用了诸如读已提交(Read Committed)、可重复读(Repeatable Read)等隔离级别。

  4. 持久性(Durability)
    一旦事务成功完成(即提交),其对数据库的修改将被永久保存,即使发生系统崩溃或其他异常,修改后的数据也不会丢失。

ACID特性是事务的基本要求,它们共同确保了数据库在并发环境下的数据完整性,使得多个用户同时操作数据库时,能够安全、正确地执行业务逻辑。在分布式系统中,实现ACID特性更具挑战性,需要借助分布式事务协调机制和数据库本身的并发控制机制来保证。

8、索引有什么缺点

索引在数据库管理系统中用于加速数据检索速度,但同时它也有以下主要缺点:

  1. 存储空间消耗:

    • 创建索引会占用额外的物理存储空间,因为索引是对数据表中一部分或全部列值的有序存储结构。数据量越大,索引占用的空间也就越大。
  2. 更新维护成本:

    • 当对表数据进行插入、删除和修改操作时,不仅要修改原数据表,还需要同步更新对应的索引结构,这会带来额外的写操作和IO开销,特别是对于大量数据的更新操作,索引维护的时间成本不容忽视。
  3. 写操作性能下降:

    • 因为每次写操作(如INSERT、UPDATE、DELETE)不仅需要更新数据表,还要维护索引,所以在频繁进行写操作的场景下,使用索引可能会降低数据库的写入性能。
  4. 降低批量导入效率:

    • 在进行大批量数据导入时,如果表上有索引,那么导入过程需要不断维护索引,这会使整个导入过程变得更慢。
  5. 索引选择不当或过度索引:

    • 如果创建了不必要的索引或者索引设计不合理(例如选择了很少用于查询条件的列作为索引),不仅浪费存储空间,还会降低查询优化器的效率,因为优化器需要花费更多时间来决定使用哪个索引来执行查询。
  6. 查询优化器误判:

    • 在某些情况下,虽然创建了索引,但如果查询优化器未能正确选择使用索引,或者查询条件不满足索引最左前缀原则,原本期望加速查询的索引反而可能不起作用。
  7. 复杂查询不一定受益:

    • 对于涉及复杂联接、多表查询、排序、分组、全表扫描等情况,索引可能并不能显著提高查询性能,甚至有可能因为索引而导致额外的性能损失。例如,对于需要进行排序的查询,如果排序字段的索引与查询条件无关,数据库可能需要先通过索引检索数据,然后再进行排序,这反而增加了处理步骤。

9、哈希索引快还是B+树索引快

哈希索引和B+树索引在查询速度上各有优劣,不能简单地说哪个绝对更快,其性能取决于具体的应用场景和查询类型:

  • 哈希索引

    • 对于等值查询(点查询),尤其是当查询的目标值是已知且哈希函数分布均匀时,哈希索引通常非常快,理想情况下查询时间复杂度接近O(1)。
    • 然而,哈希索引不支持范围查询,也无法用于排序操作,因为哈希索引的数据并非有序存储,哈希冲突也可能导致额外的查找成本。
  • B+树索引

    • B+树索引对等值查询、范围查询以及排序操作都能提供很好的支持。B+树的叶子节点是有序连接的,因此对范围查询和排序十分高效。
    • 即便在磁盘存储环境中,B+树通过多级索引结构也能有效减少磁盘I/O次数,对于复杂查询和大规模数据处理,B+树索引的整体性能更为均衡。

在内存数据库或部分内存数据库中,哈希索引由于其查找速度快的优点,在点查询上可能会表现出比B+树索引更高的性能。但在大多数关系型数据库系统中,尤其是在磁盘存储为主的环境下,B+树索引由于其对各类查询操作的良好支持和磁盘I/O效率,成为首选索引结构。在MySQL等数据库系统中,即使存在内存存储引擎(如MySQL的Memory存储引擎),一般也倾向于使用B+树索引,因为它们更能适应多样化的查询需求。

9、为什么不直接使用本地内存做缓存,而是用Redis,描述它们访问数据的流程

;