缓存与数据库的数据一致性解决方案分析

news2024/11/18 2:37:31

在现代应用中,缓存技术的使用广泛且至关重要,主要是为了提高数据访问速度和优化系统整体性能。缓存通过在内存或更快速的存储系统中存储经常访问的数据副本,使得数据检索变得迅速,从而避免了每次请求都需要从较慢的主存储(如硬盘或远程数据库)中读取数据的延迟。这种技术特别适用于读取操作远多于写入操作的场景,如网页浏览、内容分发网络(CDN)和大规模的信息检索系统等。

缓存的实现方式多样,包括但不限于内存缓存、分布式缓存和浏览器缓存等。这些缓存策略可以单独使用,也可以组合使用,以适应不同层级的需求和优化目标。例如,内存缓存通常用于存储临时的计算结果和频繁访问的小数据块,而分布式缓存则适用于大规模系统中,能够支持跨多个服务器的数据共享和管理。

此外,缓存还能够通过减少网络传输和数据库查询的次数,大幅度减轻后端服务器的负载,提高系统的并发处理能力。这在用户基数大、数据访问频繁的在线服务中尤为重要,如电子商务平台、社交媒体和在线游戏等。

但是,任何一种技术,都有它的局限性,缓存的广泛使用也带来了数据一致性的挑战。数据一致性是一个确保数据在多个复制点或过程中保持一致的属性,这在计算和数据库管理系统中至关重要。简而言之,数据一致性意味着无论数据被存储在哪里或如何被访问,都能确保数据的准确性和可靠性。

数据一致性可以分为几种类型:

  1.  强一致性:任何数据的更新操作完成后,任何后续的访问都将立即看到这些更改。这是最严格的一致性模型,通常在传统数据库系统中使用。 
  2.  最终一致性:这是一种弱一致性模型,它承诺在没有新的更新操作的情况下,最终所有的复制都将达到一致的状态。这种一致性模型通常用在分布式系统中,如云存储和大数据平台。 

本文集中探讨缓存与数据库的数据一致性问题和解决方案分析,首先明确我们要达到的目标状态,对于某个目标值:

  • 缓存和数据库都有该目标值且相等
  • 缓存没有该目标值

以上两种状态都可以算作满足了数据一致性。 

一、成因分析


缓存和数据库之间的数据不一致是分布式系统中常见的问题,这种不一致可能由多种因素引起。下面详细分析可能导致缓存和数据库数据不一致的几种情况:

单线程更新操作不同步

如果在更新(即增删改)数据库数据时,由于网络问题或者系统故障导致异步进行的缓存更新操作失败,缓存的更新操作未能成功执行,这将直接导致数据库中的数据和缓存中的数据不一致。具体可考虑以下情况:

时刻

写线程

读线程

问题

T1

数据库写入数据X,且操作成功

T2

更新缓存旧值,但由于有延迟或者由于缓存系统故障而操作失败

缓存为旧数据

T3

读取数据X

命中缓存的旧值

并发读/写操作

在高并发环境中,多个进程或线程同时对数据库和缓存进行写操作时,容易引起竞争条件。这是因为每个进程或线程都试图同时更新同一数据项,而系统的行为将依赖于不同的操作顺序,虽然这种概率极低,但如Murphy法则所描述:任何可能出错的地方终将出错。在我看来,这是对并发的本质描述了,也是正确处理并发的挑战性所在。

由于这种无法预料的行为,就可能导致缓存中的数据与数据库中的数据更新顺序不一致。例如,一个线程可能已经将最新数据写入数据库,但另一个线程可能还在读取或写入旧数据到缓存中。这种不一致会导致数据冗余和逻辑错误,用户可能读取到过时或错误的数据。

二、解决方案


不同业务场景下的数据一致性模型

强一致性、弱一致性和最终一致性是描述数据在多个地点或系统中如何保持同步的术语。它们各自对应不同的系统设计和应用场景。下面是这三种一致性级别的详细分析:

1. 强一致性(Strong Consistency)

强一致性是最严格的一致性模型,要求系统在进行了更新后,所有的访问立即看到这些更改。这意味着在一个数据项被更新之后,所有的读取操作都必须返回新的值。通常这种模型可以提供最直观和一致的用户体验,并且开发者可以假设数据在任何时候都是最新的,从而简化应用开发。

强一致性虽然提供了数据操作的最直观和一致的体验,但它也带来了一些显著的缺点,尤其是在大规模分布式系统中的可扩展性和性能,以及操作的延迟。在强一致性模型下,系统必须确保所有的数据副本在任何时候都是完全一致的。这种严格的一致性要求会导致资源大量消耗,因为系统可能需要在多个节点之间频繁地同步数据,这在多数据中心或跨地理位置分布的系统中尤其昂贵和复杂。此外,所有的写入操作必须在所有相关的副本上同步完成才能向用户报告成功,这种同步过程会形成瓶颈,限制系统处理高并发写入操作的能力,并随着系统规模的扩大,维护强一致性的复杂性和成本也会增加。此外,强一致性模型还要求每次操作都必须在所有节点之间进行协调,以确保数据的一致性,这通常涉及到复杂的协议和网络通信,如使用Raft或Paxos协议,每个写入操作都需要在多数节点上达成共识,这个过程是耗时的。在某些情况下,系统可能需要阻塞读取或写入操作直到所有的副本都更新完毕,这种阻塞会直接导致用户感受到明显的延迟。此外,如果系统的一个节点发生故障,恢复其数据和重新同步可能需要较长时间,期间系统的响应速度可能下降。这些限制使得在需要极高性能和可扩展性的应用场景中,强一致性可能不是最佳选择。

而且,当需要保持数据的强一致性时,更好的决策应该是不使用缓存,所有的操作都应该从数据读写,以保证数据的实时性和一致性。

所以,虽然缓存与数据库的强一致性模型有一些相当难以替代的有点,但是由于其代价过大,在通常的业务系统中并不需要使用这样的模型,而对于某些特定的业务场景,这些系统由于其业务的关键性和故障的巨大成本,通常才会采用强一致性模型来保证其业务数据的一致性。可以参考的一个例子是一些关键的基于数据和算法的决策系统,该系统可能会对于一定时间段的数据做出某种指导业务的决策,通常在一段时间内可能是读多写少,如果对于每次读请求都重新计算,会带来性能的巨大损耗,此时可以考虑将结果缓存。那么对于这种情况,如何保证缓存与数据库数据的强一致性呢?

强一致性解决方案分析

要解决的问题主要有两个:

  • 单线程下更新操作不同步问题
  • 并发读/写操作

要想解决以上问题,强一致性模型就必须保证更新数据库和更新缓存两者的原子性,但由于redis不支持传统意义上的事务,所以我们只能另辟蹊径。另一方面,必须保证消除或者避免并发读写产生竞争条件。

对于前者,我们可以考虑通过硬编码的形式解决,对于后者,可以考虑读写锁(分布式系统应该升级为分布式锁),兼顾一定的性能。考虑以下代码

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import redis.clients.jedis.Jedis;

@Service
public class DataService {
    @Autowired
    private JDBCTemplate jdbcTemplate;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 使用写锁保护的资源更新方法
     */
    @Transactional
    public void updateProtectedResource(String newData,int repeatCount) {
        Jedis jedis = new Jedis("localhost");
        RReadWriteLock rwLock = redissonClient.getReadWriteLock("myReadWriteLock");
        RLock writeLock = rwLock.writeLock();

        try {
            // 获取写锁
            writeLock.lock();
            // 执行写操作
            Model data = parse(newData);
            String sql = "UPDATE data_table SET name = ?, value = ? WHERE id = ?";
            jdbcTemplate.update(sql, data.getName(), data.getValue(), data.getId());
            // 更新缓存
            while (true && (repeatCount--) > 0) {
                long result = jedis.del(cacheKey);
                if (result > 0) {
                    System.out.println("Key deleted successfully.");
                    break;
                } else {
                    if(repeatCount==0){
                         throw new BussinessException("更新缓存失败,请检查");   
                    }
                    // Optional: Add some delay or max attempt logic
                    try {
                        Thread.sleep(1000);  // 等待一秒再次尝试
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        System.out.println("Thread interrupted");
                        break;
                    }
                }
            }
            // 模拟数据操作
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            // 释放写锁
            writeLock.unlock();
            jedis.close();
        }
    }

    /**
     * 使用读锁保护的资源访问方法
     */
    public void accessProtectedResource(String newData) {
        Jedis jedis = new Jedis("localhost");
        RReadWriteLock rwLock = redissonClient.getReadWriteLock("myReadWriteLock");
        RLock readLock = rwLock.readLock();

        try {
            // 获取读锁
            readLock.lock();
            // 执行读操作
            Model data = parse(newData);
            String value = jedis.get(data.getName());
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            // 释放读锁
            readLock.unlock();
            jedis.close();
        }
    }
}

该代码模拟了通过结合使用数据库事务和分布式锁来确保对Redis和MySQL之间的数据操作的强一致性,在删除Redis缓存时,添加了延时重试逻辑,如果删除失败会再次尝试,直到成功或达到重试限制,如果最终缓存删除仍然失败,代码通过抛出异常进行告警,并可以通过外部重试机制解决。这增加了操作的健壮性。

根据是否接收写请求,可以把缓存分成读写缓存和只读缓存。

  • 只读缓存:只在缓存进行数据查找,即使用“更新数据库+删除缓存”策略。
  • 读写缓存:需要在缓存中对数据进行增删改查,即使用“更新数据库+更新缓存”策略。

在模拟代码中选择了只读缓存,进一步避免了机器算力资源的浪费,提升了性能。

具体的实现机制和流程如下:

  1.  数据库事务管理: 使用@Transactional注解,该方法内的所有数据库操作都会在一个事务中执行。如果方法中的任何数据库操作失败,则整个事务会被回滚,这包括对MySQL数据库的所有更改。 
  2.  分布式锁: 代码中使用了Redisson客户端来实现分布式锁的功能。这里用到了RedissonRReadWriteLock,它是一个可重入的读写锁。在更新资源时,首先获取写锁,这会阻塞其他试图获取写锁或读锁的操作,从而保证在更新操作期间不会有其他操作可以修改或读取相关的资源。 这段代码设计用于保持Redis缓存和MySQL数据库之间的强一致性,通过使用Spring框架、JDBC模板进行数据库交互,以及Redisson客户端进行分布式锁管理。下面我们详细分析这段代码的优缺点:

当然,这也在其他方面付出了一些代价,包括:

  • 性能损耗:使用分布式写锁会阻塞所有其他的读写操作,这在高并发场景下可能会显著降低性能。尤其是在分布式系统中,锁的管理还可能增加网络延迟和复杂性。 
  • 可用性和扩展性的挑战: 
    • 在强一致性模型中,任何单点的故障都可能导致整个系统的不可用。
    • 扩展系统(尤其是水平扩展)变得更加困难,因为每个新增节点都需要加入到数据同步和一致性协议中。
    • 要使用同步复制来保持各个数据副本之间的一致性。每个写操作都必须在所有副本上确认,才能完成。
  1.  复杂性增加: 
    • 实现和维护一个强一致性系统的复杂性显著增加,这需要更多的开发和运维投入。
    • 错误处理和异常管理变得更加复杂,系统必须能够处理网络分区、节点故障等问题,并保持一致性不受影响。

总结来说,这段代码通过使用数据库事务确保MySQL操作的一致性和原子性,同时利用Redisson实现的分布式读写锁确保在更新操作期间不会有其他读写操作干扰,从而保证了在更新操作和缓存同步之间的强一致性。然而,它也带来了性能上的损耗。

2. 最终一致性(Eventual Consistency)

最终一致性是一种弱一致性的形式,保证只要没有新的更新,系统最终会达到一致的状态。更新在系统中逐渐传播,经过一段时间后,所有的副本最终将反映最新的状态。这种最终一致性适合大多数的系统,可以提高系统的可用性和扩展性,并且允许系统在部分节点故障时继续运行。只是一致性达成可能有延迟,但是在业务系统的接受范围之内。

应用场景

  • 大型分布式系统,如云存储服务和大数据处理平台。
  • 社交网络,用户的更新(如状态更新或图片上传)可以容忍短时间的不一致。

实现最终一致性的方案较多,这里列举一部分:

  1. 使用消息队列技术:如Apache Kafka或RabbitMQ,将更新操作放入队列中,然后异步处理这些更新操作,以达到最终一致性。这种方式适用于一致性要求较高的场景。
  2. 解析MySQL的binlog:通过解析MySQL的binlog来同步更新Redis。这种方式可以实现较为精确的数据同步,但需要额外的工具和技术支持。
  3. 先更新MySQL再删除Redis:这是一种较为推荐的方案,因为它产生的数据不一致概率最低,数据丢失风险最小,把控度最高。然而,这也意味着在某些情况下可能会出现短暂的数据不一致。
  4. 延时双删:这是一种更复杂的方案,它在“先删除Redis,再更新MySQL + Redis读策略”的基础上增加了最后一步Redis删除的操作。这个方案可以解决最终Redis中的数据与MySQL中的数据不一致的问题。
  5. 监控与补偿:记录所有关键操作步骤和任何异常情况在日志中,定期检查MySQL和Redis之间的数据一致性,对于异常和不一致的情况触发对应的补偿机制。

基础平台稳定性构建

系统崩溃或重启

系统崩溃或重启导致内存中的缓存数据丢失是一种常见的问题,这种情况下的数据不一致问题尤其需要关注。在系统崩溃或重启的过程中,内存中存储的所有信息(包括缓存数据)都会丢失,因为内存是易失性的存储设备。与此同时,数据库中的数据通常存储在硬盘等非易失性存储设备上,因此即使在系统崩溃后,数据库的数据依然保持不变。当系统重新启动后,如果缓存中的数据没有被适当地从数据库或其他持久存储中恢复,那么就会出现缓存与数据库之间的数据不一致问题。

为了应对系统崩溃或重启后可能出现的缓存与数据库间的数据不一致问题,可以采取以下几种策略:

  • 持久化缓存数据:某些缓存解决方案提供了持久化选项,可以将内存中的数据定期保存到硬盘上。这样,在系统重启后可以从这些持久化的数据恢复缓存,减少数据丢失的风险。
  • 缓存预热(Cache Warming):在系统启动时主动加载最常访问的数据到缓存中。这个过程称为缓存预热,它可以帮助系统更快地恢复到崩溃前的性能水平。
  • 使用备份缓存服务器:在分布式缓存解决方案中,可以通过部署多个缓存节点来防止单点故障,即使一个缓存服务器失败,其他服务器仍能提供服务。
  • 定期校验和同步:定期检查缓存与数据库之间的数据一致性,并根据需要进行同步,确保数据的准确性和最新性。

通过实施这些策略,可以最大程度地减少系统崩溃或重启对业务操作的影响,并确保数据的一致性和可靠性。这对于保持应用性能和提供高质量的用户体验是至关重要的。

缓存穿透策略

缓存穿透是指查询不存在于缓存中的数据,导致请求直接到达数据库,增加数据库的负载。解决方案包括:

  • 空对象缓存:对于查询结果为空的情况,依然将这个空结果进行缓存,防止对同一数据的重复查询。
  • 布隆过滤器:使用布隆过滤器预判数据是否存在于数据库中,不存在则拒绝查询,减少数据库压力。

缓存击穿策略

缓存击穿是指一个热点key突然过期,导致大量请求直接打到数据库上。解决方案包括:

  • 设置热点数据永不过期:针对一些热点数据设置为不过期,通过后台定时任务更新这些数据。
  • 互斥锁:当缓存失效时,不是所有的请求都去查询数据库,而是使用锁或者其他同步工具保证只有一个请求去查询数据库和重建缓存。

缓存雪崩策略

缓存雪崩是指缓存中大量的key同时过期,导致所有的请求都转到数据库上。解决方案包括:

  • 缓存数据的过期时间随机化:使得缓存的过期时间不会同时发生,避免同时大量的缓存失效。
  • 提高缓存服务的高可用性:使用集群或者分布式缓存系统,确保单点故障不会导致整个缓存服务不可用。

三、指导原则


  • 性能与可用性的权衡:强一致性通常会牺牲系统的可用性和性能,而最终一致性则可能导致短暂的数据不一致。
  • 系统复杂度:实现强一致性通常需要复杂的同步机制,可能会增加系统的实现难度和维护成本。
  • 业务需求:选择合适的一致性模型需要根据业务的具体需求。例如,金融系统可能需要更强的一致性保证,而内容分发网络则可能更倾向于最终一致性。

这些方案的选择和实现都需要根据实际的业务需求和系统环境来定制。有效的解决方案往往需要综合考虑系统的性能,可用性和一致性需求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1594691.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

labview中的同步定时结构

单帧定时循环定时比较精确,最常用的功能还是它的定时循环功能,定时循环允许不连接“循环条件”端子,可以连接定时循环“结构名称”端子,通过定时结构停止函数停止循环。 例子在附件中。

MySQL workbench使用教程(逐渐补充版)

附件: 附1:MySQL下载、安装、配置之Windows 附2:MySQL workbench下载、安装、配置、汉化教程 一、 使用 Workbench 操作数据库 1.MySQL Workbench 初始化界面 2.连接远程 MySQL 数据库 3.创建数据库 切换至schemas标签,右键单…

在家如何查找下载外文文献

查找下载外文文献的数据库大部分都需要使用权限的,那么我们如何在家进入这些数据库查找下载文献资源呢?请看本文的经验分享: 举例1、 一位同学的文献求助:Performance of financial hedging and earnings management under dive…

科研学习|科研软件——如何使用SmartPLS软件进行结构方程建模

SmartPLS是一种用于结构方程建模(SEM)的软件,它可以用于定量研究,尤其是在商业和社会科学领域中,如市场研究、管理研究、心理学研究等。 一、准备数据 在使用SmartPLS之前,您需要准备一个符合要求的数据集。…

【自研网关系列】请求服务模块和客户端模块实现

🌈Yu-Gateway::基于 Netty 构建的自研 API 网关,采用 Java 原生实现,整合 Nacos 作为注册配置中心。其设计目标是为微服务架构提供高性能、可扩展的统一入口和基础设施,承载请求路由、安全控制、流量治理等…

EasyUI Jquery 学习笔记 ——DataGrid(数据网格)与 Tree(树)详细版

1. DataGrid(数据网格)与 Tree(树) 1.1 Datagrid 数据网格 扩展自 $.fn.panel.defaults。通过 $.fn.datagrid.defaults 重写默认的 defaults。 数据网格(datagrid)以表格格式显示数据,并为选择、排序、分组和编辑数据提供了丰富的支持。数据网格(datagrid)的设计目…

前端三件套学习笔记(持更)

目录 1、HTML,CSS,JS区别 2、HTML结构 1、HTML,CSS,JS区别 结构写到 HTML 文件中&#xff0c; 表现写到 CSS 文件中&#xff0c; 行为写到 JavaScript文件中。 2、HTML结构 <!DOCTYPE html> <html><head><title>我的第一个页面</title><…

[尚硅谷flink] 检查点笔记

在Flink中&#xff0c;有一套完整的容错机制来保证故障后的恢复&#xff0c;其中最重要的就是检查点。 文章目录 11.1 检查点11.1.1 检查点的保存1&#xff09;周期性的触发保存2&#xff09;保存的时间点3&#xff09;保存的具体流程 11.1.2 从检查点恢复状态11.1.3 检查点算法…

计算机服务器中了rmallox勒索病毒怎么办,rmallox勒索病毒解密流程步骤

在企业的生产运营过程中网络发挥着巨大作用&#xff0c;利用网络可以拓宽市场&#xff0c;提高办公效率&#xff0c;网络为企业的生产运营提供了极大便利&#xff0c;但也为企业的数据安全带来隐患。近日&#xff0c;云天数据恢复中心接到多家企业的求助&#xff0c;企业的计算…

二维相位解包理论算法和软件【全文翻译- 菲林(Flynn)最小不连续性方法(4.5)】

4.5 菲林最小不连续性方法 在迄今为止对路径跟踪算法的讨论中,我们忽略了一种非常自然的方法,现在我们将对其进行描述。如果我们仔细观察图 4.42(a)中包裹相位数据中的条纹图案,就会发现 "条纹线 "或最亮像素和最暗像素之间的边界标志着从 0 到 2π 的过渡,它们…

Linux gcc 6

本章开始学习工具 什么是工具&#xff1f; 本质也是指令 yum 命令 小火车 sudo yum install sl&#xff08;安装sl&#xff09; sudo yum install -y sl //直接yes就不提示了 yum list //将yum源上的软件都穷举出来 yum search sl //结果不友好&#xff0c;不推荐 yum lis…

智能革命:未来人工智能创业的天地

智能革命&#xff1a;未来人工智能创业的天地 一、引言 在这个数字化迅速变革的时代&#xff0c;人工智能(AI)已经从一个边缘科学发展成为推动未来经济和社会发展的关键动力。这一技术领域的飞速进步&#xff0c;不仅影响着科技行业的每一个角落&#xff0c;更是为创业者提供了…

PTA 2813:画家问题(熄灯问题)

有一个正方形的墙&#xff0c;由NN个正方形的砖组成&#xff0c;其中一些砖是白色的&#xff0c;另外一些砖是黄色的。Bob是个画家&#xff0c;想把全部的砖都涂成黄色。但他的画笔不好使。当他用画笔涂画第(i,j)个位置的砖时&#xff0c; 位置(i−1,j)、 (i1,j)、(i,j−1)、(i…

设计模式学习笔记 - 设计模式与范式 -行为型:17.中介模式:什么时候用中介模式?什么时候用观察者模式?

概述 本章学习 23 种经典设计模式中的最后一个设计模式&#xff0c;中介模式。和之前讲过的命令模式、解释器模式类似&#xff0c;中介模式也不怎么常用&#xff0c;应用场景比较特殊、有限&#xff0c;但是&#xff0c;跟它俩不同的是&#xff0c;中介模式理解起来并不难&…

《手把手教你》系列基础篇(八十六)-java+ selenium自动化测试-框架设计基础-Log4j实现日志输出(详解教程)

1.简介 自动化测试中如何输出日志文件。任何软件&#xff0c;都会涉及到日志输出。所以&#xff0c;在测试人员报bug&#xff0c;特别是崩溃的bug&#xff0c;一般都要提供软件产品的日志文件。开发通过看日志文件&#xff0c;知道这个崩溃产生的原因&#xff0c;至少知道触发崩…

图文教程 | 2024Typora最新版免费激活使用教程(新旧版可用)

一、打开官网下载最新版Typora Typora 官网下载 安装&#xff1a; Typora中文官网&#xff1a;https://typoraio.cn/ Typora官网&#xff1a;https://typora.io/releases/all 官网长这个样子 下面这个不是官网&#xff01;&#xff01;&#xff01;&#xff01;注意&#x…

《一》Qt的概述

1.1 什么是Qt Qt是一个跨平台的C图形用户界面应用程序框架。它为应用程序开发者提供建立图形界面所需的所有功能。它是完全面向对象的&#xff0c;很容易扩展&#xff0c;并且允许真正的组件编程。 1.2 Qt的发展史 1991年 Qt最早由芬兰奇趣科技开发 1996年 进入商业领域&#x…

【Django开发】0到1美多商城项目md教程第7篇:登录,1. 互联开发者申请步骤【附代码文档】

美多商城完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;欢迎来到美多商城&#xff01;&#xff0c;项目准备。展示用户注册页面&#xff0c;创建用户模块子应用。用户注册业务实现&#xff0c;用户注册前端逻辑。图形验证码&#xff0c;图形验证码接口设…

结合 react-webcam、three.js 与 electron 实现桌面人脸动捕应用

系列文章目录 React 使用 three.js 加载 gltf 3D模型 | three.js 入门React three.js 3D模型骨骼绑定React three.js 3D模型面部表情控制React three.js 实现人脸动捕与3D模型表情同步结合 react-webcam、three.js 与 electron 实现桌面人脸动捕应用 示例项目(github)&…

Jackson 2.x 系列【19】模块 Module

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Jackson 版本 2.17.0 源码地址&#xff1a;https://gitee.com/pearl-organization/study-jaskson-demo 文章目录 1. 前言2. 核心类2.1 Module2.2 SimpleModule 3. 案例演示3.1 自定义模块3.2 注册…