当前位置: 首页 > article >正文

缓存与数据库数据一致性:旁路缓存、读写穿透和异步写入模式解析

旁路缓存模式、读写穿透模式和异步缓存写入模式是三种常见的缓存使用模式,以下是对三种经典缓存使用模式在缓存与数据库数据一致性方面更全面的分析:

一、旁路缓存模式(Cache - Aside Pattern)

1.数据读取流程

  • 应用程序首先向缓存发送读取请求,检查所需数据是否在缓存中。
  • 如果缓存命中,直接从缓存中获取数据并返回给应用程序,这能极大提高读取速度,减少数据库的负载。
  • 若缓存未命中,应用程序接着向数据库发送读取请求,从数据库获取数据。获取到数据后,一方面将数据返回给应用程序,另一方面把数据写入缓存,同时可以设置缓存数据的过期时间,以便在数据更新后能及时从数据库重新获取最新数据。

2.数据写入流程

  • 当应用程序要更新数据时,首先更新数据库中的数据,确保数据库作为数据的可靠来源得到及时更新。
  • 在数据库更新成功后,立即删除缓存中对应的旧数据。这样做是为了让下次读取该数据时,能从数据库获取到最新数据并更新到缓存中,保证缓存数据的时效性。

3.一致性分析

  • 优点
    • 实现相对简单,在正常情况下能较好地保证数据一致性。以数据库为数据的权威来源,缓存主要用于加速读取,通过先更新数据库再删除缓存的操作顺序,多数情况下能确保缓存数据要么是最新的,要么不存在,等待下次读取时更新。
    • 读性能优化明显,缓存命中时能快速响应读取请求,减轻数据库压力,适用于读多写少的场景。
  • 缺点
    • 在高并发场景下可能出现数据不一致问题。例如,两个并发更新操作同时对同一数据进行修改,若操作 A 先更新数据库但在删除缓存前,操作 B 更新数据库并先于操作 A 删除缓存,接着操作 A 再删除缓存,此时缓存中无最新数据,读取请求可能获取到旧数据,直到下次缓存更新。
    • 缓存数据的过期时间设置较为关键,若设置过长,可能导致缓存数据长时间不一致;设置过短,则会增加数据库的读取压力。

4.代码实例

import redis.clients.jedis.Jedis;
import java.sql.*;public class CacheAsidePattern {private static final String REDIS_HOST = "localhost";private static final int REDIS_PORT = 6379;private static final String DB_URL = "jdbc:mysql://localhost:3306/testdb";private static final String DB_USER = "root";private static final String DB_PASSWORD = "password";public static void main(String[] args) {try (Jedis jedis = new Jedis(REDIS_HOST, REDIS_PORT);Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD)) {// 创建表createTable(conn);// 插入示例数据insertSampleData(conn);// 测试读取String userName = getUser(jedis, conn, 1);System.out.println("用户姓名: " + userName);// 测试更新updateUser(jedis, conn, 1, "Bob");userName = getUser(jedis, conn, 1);System.out.println("更新后用户姓名: " + userName);} catch (SQLException e) {e.printStackTrace();}}private static void createTable(Connection conn) throws SQLException {String createTableSQL = "CREATE TABLE IF NOT EXISTS users (" +"id INT PRIMARY KEY, " +"name VARCHAR(255))";try (Statement stmt = conn.createStatement()) {stmt.executeUpdate(createTableSQL);}}private static void insertSampleData(Connection conn) throws SQLException {String insertSQL = "INSERT IGNORE INTO users (id, name) VALUES (1, 'Alice')";try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) {pstmt.executeUpdate();}}private static String getUser(Jedis jedis, Connection conn, int userId) {String key = "user:" + userId;String user = jedis.get(key);if (user != null) {System.out.println("从缓存中获取数据");return user;} else {try (PreparedStatement pstmt = conn.prepareStatement("SELECT name FROM users WHERE id = ?")) {pstmt.setInt(1, userId);ResultSet rs = pstmt.executeQuery();if (rs.next()) {user = rs.getString("name");jedis.set(key, user);System.out.println("从数据库中获取数据并写入缓存");return user;}} catch (SQLException e) {e.printStackTrace();}return null;}}private static void updateUser(Jedis jedis, Connection conn, int userId, String newName) {String key = "user:" + userId;try (PreparedStatement pstmt = conn.prepareStatement("UPDATE users SET name = ? WHERE id = ?")) {pstmt.setString(1, newName);pstmt.setInt(2, userId);pstmt.executeUpdate();jedis.del(key);System.out.println("数据库更新并删除缓存");} catch (SQLException e) {e.printStackTrace();}}
}    

二、读写穿透模式(Write - Through Pattern)

1.数据读取流程

  • 应用程序向缓存发送读取请求,尝试从缓存中获取数据。
  • 如果缓存命中,直接从缓存中返回数据给应用程序。
  • 若缓存未命中,应用程序从数据库读取数据,读取到数据后,将数据返回给应用程序,同时将数据写入缓存,且写入缓存操作是同步进行的,确保数据在缓存和数据库中同时更新。

2.数据写入流程

  • 当应用程序执行写操作时,会同时向缓存和数据库发送更新请求。先将数据写入缓存,然后由缓存负责将数据同步到数据库,通常通过缓存的写入操作触发对数据库的写入,保证缓存和数据库数据的实时同步。

3.一致性分析

  • 优点
    • 能严格保证数据一致性,每次读写操作都确保缓存和数据库的数据同步更新,两者就像一个整体,任何一方的更新立即反映到另一方,不存在数据延迟或不一致的情况。
    • 对于读写操作较为均衡的场景,该模式能较好地适应,不会出现因写操作频繁导致缓存与数据库数据不一致的问题。
  • 缺点
    • 由于缓存和数据库是不同存储系统,其写入性能和可靠性存在差异,可能出现缓存写入成功但数据库写入失败的情况,导致数据不一致。
    • 为保证一致性引入的补偿机制,如重试机制或事务机制,会增加系统复杂性和开发成本。同时,同步写入操作可能会降低写操作的性能,因为需要等待数据库写入完成才能返回结果。

4.代码实例

import redis.clients.jedis.Jedis;
import java.sql.*;public class WriteThroughPattern {private static final String REDIS_HOST = "localhost";private static final int REDIS_PORT = 6379;private static final String DB_URL = "jdbc:mysql://localhost:3306/testdb";private static final String DB_USER = "root";private static final String DB_PASSWORD = "password";public static void main(String[] args) {try (Jedis jedis = new Jedis(REDIS_HOST, REDIS_PORT);Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD)) {// 创建表createTable(conn);// 插入示例数据insertSampleData(conn);// 测试读取String userName = getUser(jedis, conn, 1);System.out.println("用户姓名: " + userName);// 测试更新updateUser(jedis, conn, 1, "Bob");userName = getUser(jedis, conn, 1);System.out.println("更新后用户姓名: " + userName);} catch (SQLException e) {e.printStackTrace();}}private static void createTable(Connection conn) throws SQLException {String createTableSQL = "CREATE TABLE IF NOT EXISTS users (" +"id INT PRIMARY KEY, " +"name VARCHAR(255))";try (Statement stmt = conn.createStatement()) {stmt.executeUpdate(createTableSQL);}}private static void insertSampleData(Connection conn) throws SQLException {String insertSQL = "INSERT IGNORE INTO users (id, name) VALUES (1, 'Alice')";try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) {pstmt.executeUpdate();}}private static String getUser(Jedis jedis, Connection conn, int userId) {String key = "user:" + userId;String user = jedis.get(key);if (user != null) {System.out.println("从缓存中获取数据");return user;} else {try (PreparedStatement pstmt = conn.prepareStatement("SELECT name FROM users WHERE id = ?")) {pstmt.setInt(1, userId);ResultSet rs = pstmt.executeQuery();if (rs.next()) {user = rs.getString("name");jedis.set(key, user);System.out.println("从数据库中获取数据并写入缓存");return user;}} catch (SQLException e) {e.printStackTrace();}return null;}}private static void updateUser(Jedis jedis, Connection conn, int userId, String newName) {String key = "user:" + userId;try {conn.setAutoCommit(false);jedis.set(key, newName);try (PreparedStatement pstmt = conn.prepareStatement("UPDATE users SET name = ? WHERE id = ?")) {pstmt.setString(1, newName);pstmt.setInt(2, userId);pstmt.executeUpdate();}conn.commit();System.out.println("缓存和数据库同时更新");} catch (SQLException e) {try {conn.rollback();} catch (SQLException rollbackEx) {rollbackEx.printStackTrace();}System.out.println("更新失败: " + e.getMessage());}}
}    

三、异步缓存写入模式(Write - Behind Caching Pattern)

1.数据读取流程

  • 与前两种模式类似,应用程序首先从缓存中读取数据。
  • 若缓存命中,直接返回数据。
  • 缓存未命中时,从数据库读取数据并返回给应用程序,同时将数据写入缓存。

2.数据写入流程

  • 写操作发生时,应用程序只将数据写入缓存,然后由缓存负责在后台异步地将数据批量写入数据库。可以根据一定的策略,如达到一定的写入次数或经过一定的时间间隔,将缓存中的数据批量刷写到数据库。

3.一致性分析

  • 优点
    • 写性能极高,应用程序无需等待数据库写入完成即可快速响应写请求,能显著提高系统吞吐量,适用于写操作频繁的场景,如日志记录、实时数据采集等。
    • 通过批量写入数据库,减少了数据库的写入次数,降低了数据库的 I/O 压力,有助于提高数据库的性能和稳定性。
  • 缺点
    • 数据一致性问题较为严重。由于数据是异步写入数据库的,在写入缓存后到写入数据库之前的时间段内,若发生系统故障、缓存数据丢失或缓存服务崩溃等情况,可能导致数据丢失,破坏数据一致性。
    • 为保证数据一致性采取的措施,如持久化缓存、合理设置缓存刷写策略、系统恢复时的数据恢复操作等,增加了系统的复杂性和运维成本。同时,还需考虑数据库写入的并发控制,避免数据冲突和不一致。

4.代码实例

import redis.clients.jedis.Jedis;
import java.sql.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class WriteBehindCachingPattern {private static final String REDIS_HOST = "localhost";private static final int REDIS_PORT = 6379;private static final String DB_URL = "jdbc:mysql://localhost:3306/testdb";private static final String DB_USER = "root";private static final String DB_PASSWORD = "password";private static final int FLUSH_INTERVAL = 5; // 每 5 秒刷写一次public static void main(String[] args) {try (Jedis jedis = new Jedis(REDIS_HOST, REDIS_PORT);Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD)) {// 创建表createTable(conn);// 插入示例数据insertSampleData(conn);// 启动异步刷写任务ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);executor.scheduleAtFixedRate(() -> flushCacheToDB(jedis, conn), 0, FLUSH_INTERVAL, TimeUnit.SECONDS);// 测试读取String userName = getUser(jedis, conn, 1);System.out.println("用户姓名: " + userName);// 测试更新updateUser(jedis, conn, 1, "Bob");userName = getUser(jedis, conn, 1);System.out.println("更新后用户姓名: " + userName);// 关闭线程池executor.shutdown();} catch (SQLException e) {e.printStackTrace();}}private static void createTable(Connection conn) throws SQLException {String createTableSQL = "CREATE TABLE IF NOT EXISTS users (" +"id INT PRIMARY KEY, " +"name VARCHAR(255))";try (Statement stmt = conn.createStatement()) {stmt.executeUpdate(createTableSQL);}}private static void insertSampleData(Connection conn) throws SQLException {String insertSQL = "INSERT IGNORE INTO users (id, name) VALUES (1, 'Alice')";try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) {pstmt.executeUpdate();}}private static String getUser(Jedis jedis, Connection conn, int userId) {String key = "user:" + userId;String user = jedis.get(key);if (user != null) {System.out.println("从缓存中获取数据");return user;} else {try (PreparedStatement pstmt = conn.prepareStatement("SELECT name FROM users WHERE id = ?")) {pstmt.setInt(1, userId);ResultSet rs = pstmt.executeQuery();if (rs.next()) {user = rs.getString("name");jedis.set(key, user);System.out.println("从数据库中获取数据并写入缓存");return user;}} catch (SQLException e) {e.printStackTrace();}return null;}}private static void updateUser(Jedis jedis, Connection conn, int userId, String newName) {String key = "user:" + userId;jedis.set(key, newName);System.out.println("数据写入缓存,等待异步刷写数据库");}private static void flushCacheToDB(Jedis jedis, Connection conn) {try {conn.setAutoCommit(false);// 模拟获取所有用户缓存数据// 实际应用中需要根据业务逻辑获取待刷写的数据String keyPattern = "user:*";for (String key : jedis.keys(keyPattern)) {int userId = Integer.parseInt(key.split(":")[1]);String userName = jedis.get(key);try (PreparedStatement pstmt = conn.prepareStatement("UPDATE users SET name = ? WHERE id = ?")) {pstmt.setString(1, userName);pstmt.setInt(2, userId);pstmt.executeUpdate();}}conn.commit();System.out.println("缓存数据刷写到数据库");} catch (SQLException e) {try {conn.rollback();} catch (SQLException rollbackEx) {rollbackEx.printStackTrace();}System.out.println("刷写失败: " + e.getMessage());}}
}    

三种经典缓存使用模式在缓存与数据库数据一致性方面各有优劣。在实际应用中,需要根据业务对数据一致性的严格程度、读写操作的频率和性能要求等因素,综合权衡选择合适的缓存模式,并通过相应的技术手段和策略来最大程度地保障数据一致性。

相关文章:

缓存与数据库数据一致性:旁路缓存、读写穿透和异步写入模式解析

旁路缓存模式、读写穿透模式和异步缓存写入模式是三种常见的缓存使用模式,以下是对三种经典缓存使用模式在缓存与数据库数据一致性方面更全面的分析: 一、旁路缓存模式(Cache - Aside Pattern) 1.数据读取流程 应用程序首先向缓…...

【物联网】基于LORA组网的远程环境监测系统设计(机智云版)

基于LORA组网的远程环境监测系统设计(机智云版) 演示视频: 简介: 1.本系统有一个主机,两个从机。 2.一主多从的LORA组网通信,主机和两个从机都配备了STM32F103单片机与 LoRa 模块,主机作为中心设备及WIFI网关,负责接收和发送数据到远程物联网平台和手机APP,两个从机…...

Pygame事件处理详解:键盘、鼠标与自定义事件

Pygame事件处理详解:键盘、鼠标与自定义事件 在游戏开发中,玩家的交互是至关重要的。无论是移动角色、触发动作还是暂停游戏,都需要通过各种输入来实现。Pygame作为一个功能强大的Python库,提供了丰富的API来处理这些输入,包括键盘、鼠标以及自定义事件。本文将详细介绍如…...

制作一款打飞机游戏22:表格导出

编辑器功能扩展 今天,我想让编辑器能够处理一个数组,这是编辑器将要编辑的东西,它只编辑数组。这些区域在后续的不同版本的编辑器中会有不同的含义,但现在我想创建一个模板,能够加载一个二维数组,并将二维…...

Linux内核源码结构

目录 Linux内核源码结构 Linux内核版本命名 Linux内核版本选择 内核源码结构 arch:与CPU架构相关的源代码 block:磁盘设备的支持 COPYING文件 CREDITS文件 crypto:加密相关 Documentation: drivers:设备驱动 firmware:固件 fs:文件系统 include:头文件…...

72.评论日记

【巫师】中美关税战02:应给人民爆装备,以及普通人如何应对(7条建议)_哔哩哔哩_bilibili 2025年4月26日11:03:31...

在springboot项目中,如何进行excel表格的导入导出功能?

以下是使用 Apache POI 和 EasyExcel 实现 Excel 表格导入导出功能的具体代码示例。 1. 使用 Apache POI 实现 Excel 导入导出 添加依赖 在 pom.xml 中添加 Apache POI 的依赖&#xff1a; <dependency><groupId>org.apache.poi</groupId><artifactId…...

Websocket自动发送消息客户端工具

点击下载《Websocket自动发送消息客户端工具》 1. 前言 在现代网络应用中&#xff0c;实时通信和即时数据传输变得越来越重要。WebSocket作为一种全双工通信协议&#xff0c;因其高效、实时的特点&#xff0c;被广泛应用于聊天应用、实时数据监控、在线游戏等领域。然而&…...

STM32的开发环境介绍

目录 STM32软件环境 Keil软件在线安装 其他软件环境安装 STM32开发的几种方式 STM32寄存器版本和库函数版本 标准外设库的作用&#xff1a; STM32软件环境 STM32 的集成开发环境&#xff08;IDE&#xff09;&#xff1a;编辑编译软件 常见的环境&#xff1a; (1)KEIL&a…...

数据库系统概论(四)关系操作,关系完整性与关系代数

数据库系统概论&#xff08;四&#xff09;详细讲解关系操作&#xff0c;关系完整性与关系代数 前言一、什么是关系操作1.1 基本的关系操作1.2 关系数据语言的分类有哪些 二、关系的完整性2.1 实体完整性2.2 参照完整性2.3 用户的定义完整性 三、关系代数是什么3.1 传统的集合运…...

基于 IPMI + Kickstart + Jenkins 的 OS 自动化安装

Author&#xff1a;Arsen Date&#xff1a;2025/04/26 目录 环境要求实现步骤自定义 ISO安装 ipmitool安装 NFS定义 ks.cfg安装 HTTP编写 Pipeline 功能验证 环境要求 目标服务器支持 IPMI / Redfish 远程管理&#xff08;如 DELL iDRAC、HPE iLO、华为 iBMC&#xff09;&…...

【AI提示词】财务顾问

提示说明 财务顾问是一个专注于帮助个人和企业优化财务状况、制定财务计划并实现财务目标的专业人士。 提示词 # Role: 财务顾问## Profile - language: 中文 - description: 财务顾问是一个专注于帮助个人和企业优化财务状况、制定财务计划并实现财务目标的专业人士。 - ba…...

使用 Node、Express 和 MongoDB 构建一个项目工程

本文将详细介绍如何使用 Node.js Express MongoDB 构建一个完整的 RESTful API 后端项目&#xff0c;涵盖&#xff1a; 项目初始化 Express 服务器搭建 MongoDB 数据库连接 REST API 设计&#xff08;CRUD 操作&#xff09; 错误处理与中间件 源码结构与完整代码 部署建…...

【C++11】右值引用和移动语义:万字总结

&#x1f4dd;前言&#xff1a; 这篇文章我们来讲讲右值引用和移动语义 &#x1f3ac;个人简介&#xff1a;努力学习ing &#x1f4cb;个人专栏&#xff1a;C学习笔记 &#x1f380;CSDN主页 愚润求学 &#x1f304;其他专栏&#xff1a;C语言入门基础&#xff0c;python入门基…...

【滑动窗口+哈希表/数组记录】Leetcode 3. 无重复字符的最长子串

题目要求 给定一个字符串 s&#xff0c;找出其中不含有重复字符的最长子串的长度。 子字符串是字符串中连续非空字符序列。 示例 1 输入&#xff1a;s "abcabcbb" 输出&#xff1a;3 解释&#xff1a;无重复字符的最长子串是 "abc"&#xff0c;长度为…...

pytest 技术总结

目录 一 pytest的安装&#xff1a; 二 pytest有三种启动方式&#xff1a; 三 用例规则&#xff1a; 四 配置框架&#xff1a; 一 pytest的安装&#xff1a; pip install pytest # 安装 pip install pytest -U # 升级到最新版 二 pytest有三种启动方式&#xff1a; 1…...

java中的Selector详解

Selector(选择器)是Java NIO(非阻塞I/O)的核心组件,用于实现I/O多路复用,允许单个线程管理多个通道(Channel),从而高效处理高并发场景。 一、Selector的核心概念与作用 I/O多路复用 Selector通过事件驱动机制,监听多个通道的就绪状态(如可读、可写、连接建立等),无…...

DeepSeek 的长上下文扩展机制

DeepSeek 在基础预训练完成后,引入 YaRN(Yet another RoPE extensioN method)技术,通过额外的训练阶段将模型的上下文窗口从默认的 4K 逐步扩展至 128K。整个过程分为两个阶段:第一阶段将上下文窗口从 4K 扩展到 32K;第二阶段则进一步从 32K 扩展到 128K。每个阶段均采用…...

【修复】Django收到请求报Json解析错误

Django收到请求报Json解析错误 场景分析解决 场景 在使用Postman发送Django的请求时&#xff0c;只能使用原来的json内容&#xff0c;如果修改json内容则会报json解析上的错误 分析 可能是有对请求内容的长度做了上报校验 解决 最终在请求头Headers里找到了Content-Length…...

openEuler对比CentOS的核心优势分析

openEuler对比CentOS的核心优势分析 在开源操作系统领域&#xff0c;openEuler与CentOS均占据重要地位&#xff0c;但随着CentOS维护策略的调整&#xff08;如CentOS 8停止维护&#xff0c;转向CentOS Stream&#xff09;&#xff0c;越来越多的用户开始关注国产化替代方案。o…...

Python基于Django的全国二手房可视化分析系统【附源码】

博主介绍&#xff1a;✌Java老徐、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&…...

VulnHub-DC-2靶机渗透教程

VulnHub-DC-2靶机渗透教程 1.靶机部署 [Onepanda] Mik1ysomething 靶机下载&#xff1a;https://download.vulnhub.com/dc/DC-2.zip 直接使用VMware导入打开就行 2.信息收集 2.1 获取靶机ip(arp-scan/nmap) arp-scan -l ​ nmap 192.168.135.0/24 2.2 详细信息扫描(nmap)…...

n8n 中文系列教程_10. 解析n8n中的AI节点:从基础使用到高级Agent开发

在自动化工作流中集成AI能力已成为提升效率的关键。n8n通过内置的LangChain节点&#xff0c;让开发者无需复杂代码即可快速接入GPT-4、Claude等大模型&#xff0c;实现文本处理、智能决策等高级功能。本文将深入解析n8n的AI节点体系&#xff0c;从基础的Basic LLM Chain到强大的…...

Jest 快照测试

以下是关于 Jest 快照测试的系统化知识总结,从基础使用到底层原理全面覆盖: 一、快照测试核心原理 1. 工作机制三阶段 #mermaid-svg-GC46t2NBvGv7RF0M {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GC46t2NBvGv…...

Linux内核参数调优(TCP BBR算法实践)

Linux 内核参数调优中 TCP BBR 算法的深度实践指南,包含原理说明、操作步骤、性能验证及生产环境注意事项: 一、BBR 算法原理 核心思想 基于拥塞状态而非丢包:传统 CUBIC 算法依赖丢包判断拥塞,BBR 通过测量带宽 (Bandwidth) 和 RTT (Round-Trip Time) 动态调整发送速率。…...

计算机网络 | 应用层(1)--应用层协议原理

&#x1f493;个人主页&#xff1a;mooridy &#x1f493;专栏地址&#xff1a;《计算机网络&#xff1a;自定向下方法》 大纲式阅读笔记 关注我&#x1f339;&#xff0c;和我一起学习更多计算机的知识 &#x1f51d;&#x1f51d;&#x1f51d; 目录 1. 应用层协议原理 1.1 …...

MuJoCo 关节角速度记录与可视化,监控机械臂运动状态

视频讲解&#xff1a; MuJoCo 关节角速度记录与可视化&#xff0c;监控机械臂运动状态 代码仓库&#xff1a;GitHub - LitchiCheng/mujoco-learning 关节空间的轨迹优化&#xff0c;实际上是对于角速度起到加减速规划的控制&#xff0c;故一般来说具有该效果的速度变化会显得丝…...

精益数据分析(27/126):剖析用户价值与商业模式拼图

精益数据分析&#xff08;27/126&#xff09;&#xff1a;剖析用户价值与商业模式拼图 在创业和数据分析的领域中&#xff0c;每一次深入学习都是一次成长的契机。今天&#xff0c;我们继续秉持共同进步的理念&#xff0c;深入研读《精益数据分析》&#xff0c;剖析用户价值的…...

Neo4j 常用查询语句

Neo4j 常用查询语句 Neo4j 是一个图数据库&#xff0c;查询语言是 Cypher&#xff0c;它类似于 SQL 但针对图形数据进行了优化。Cypher 语法直观易懂&#xff0c;适合用来处理图数据。本文将介绍一些 Neo4j 中常用的查询语句&#xff0c;帮助你快速掌握图数据的操作方法。 一…...

LVGL模拟器:NXP GUIDER+VSCODE

1. 下载安装包 NXP GUIDER&#xff1a;GUI Guider | NXP 半导体 CMAKE&#xff1a;Download CMake MINGW&#xff1a;https://github.com/niXman/mingw-builds-binaries/releases SDL2&#xff1a;https://github.com/libsdl-org/SDL/releases/tag/release-2.30.8 VSCODE&…...