株洲网站建设兼职/百度官网首页登录入口
基于消息队列实现分布式事务,实现消息最终一致性
如何基于消息队列实现分布式事务?
通过消息队列实现分布式事务的话,可以保证当前数据的最终一致性。实现思路:将大的分布式事务,进行拆分,拆分成若干个小的本地事务,只要每一个小的本地事务是执行成功的,那就代表当前的分布式事务是执行成功的。下面以用户下单增加积分,来进行消息队列实现分布式事务的操作。
功能实现流程图如下:
主要包含三个角色:订单服务,消息队列和用户服务。
步骤(1-4):订单服务
1.首先,在订单服务中,会生成订单相关数据,并添加到数据库中。
2.接着会在订单数据库中,创建任务表 ,会向任务表添加数据,(Username,orderID,point积分)
3.设置定时任务,扫描任务表,获取相关数据,如每隔7S扫描一次
4.发送任务数据到MQ
步骤(5-12):用户服务
5.6.7.用户服务会接收消息,保证消息的不可重复消费,判断在Redis和 在数据库中是否存在。防止重复处理,不要重复添加积分。
用户服务正在执行消息
如果不存在,继续业务功能。
添加本地事务
12.通知订单服务
步骤(13):订单服务
13.删除任务表数据。
一、 准备工作
1.1 shangcheng_order库新增数据表
任务表:
DROP TABLE IF EXISTS `tb_task`;
CREATE TABLE `tb_task` (`id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '任务id',`create_time` datetime DEFAULT NULL,`update_time` datetime DEFAULT NULL,`delete_time` datetime DEFAULT NULL,`task_type` varchar(32) DEFAULT NULL COMMENT '任务类型',`mq_exchange` varchar(64) DEFAULT NULL COMMENT '交换机名称',`mq_routingkey` varchar(64) DEFAULT NULL COMMENT 'routingkey',`request_body` varchar(512) DEFAULT NULL COMMENT '任务请求的内容',`status` varchar(32) DEFAULT NULL COMMENT '任务状态',`errormsg` varchar(512) DEFAULT NULL COMMENT '任务错误信息',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
历史任务表:
DROP TABLE IF EXISTS `tb_task_his`;
CREATE TABLE `tb_task_his` (`id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '任务id',`create_time` datetime DEFAULT NULL,`update_time` datetime DEFAULT NULL,`delete_time` datetime DEFAULT NULL,`task_type` varchar(32) DEFAULT NULL COMMENT '任务类型',`mq_exchange` varchar(64) DEFAULT NULL COMMENT '交换机名称',`mq_routingkey` varchar(64) DEFAULT NULL COMMENT 'routingkey',`request_body` varchar(512) DEFAULT NULL COMMENT '任务请求的内容',`status` varchar(32) DEFAULT NULL COMMENT '任务状态',`errormsg` varchar(512) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
1.2 shangcheng_service_order_api添加相关实体类
@Table(name="tb_task")
public class Task {
@Idprivate Long id;
@Column(name = "create_time")private Date createTime;
@Column(name = "update_time")private Date updateTime;
@Column(name = "delete_time")private Date deleteTime;
@Column(name = "task_type")private String taskType;
@Column(name = "mq_exchange")private String mqExchange;
@Column(name = "mq_routingkey")private String mqRoutingkey;
@Column(name = "request_body")private String requestBody;
@Column(name = "status")private String status;
@Column(name = "errormsg")private String errormsg;//getter,setter略
}
@Table(name="tb_task_his")
public class TaskHis {
@Idprivate Long id;
@Column(name = "create_time")private Date createTime;
@Column(name = "update_time")private Date updateTime;
@Column(name = "delete_time")private Date deleteTime;
@Column(name = "task_type")private String taskType;
@Column(name = "mq_exchange")private String mqExchange;
@Column(name = "mq_routingkey")private String mqRoutingkey;
@Column(name = "request_body")private String requestBody;
@Column(name = "status")private String status;
@Column(name = "errormsg")private String errormsg;
//getter,setter略
}
1.3 shangcheng_user新增积分日志表
DROP TABLE IF EXISTS `tb_point_log`;
CREATE TABLE `tb_point_log` (`order_id` varchar(200) NOT NULL,`user_id` varchar(200) NOT NULL,`point` int(11) NOT NULL,PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
1.4 shangcheng_service_user_api添加实体类 PointLog
@Table(name="tb_point_log")
public class PointLog {
private String orderId;private String userId;private Integer point;//getter,setter略
}
1.5 shangcheng_service_order添加rabbitMQ配置类
@Configuration
public class RabbitMQConfig {//添加积分任务交换机public static final String EX_BUYING_ADDPOINTUSER = "ex_buying_addpointuser";
//添加积分消息队列public static final String CG_BUYING_ADDPOINT = "cg_buying_addpoint";
//完成添加积分消息队列public static final String CG_BUYING_FINISHADDPOINT = "cg_buying_finishaddpoint";
//添加积分路由keypublic static final String CG_BUYING_ADDPOINT_KEY = "addpoint";
//完成添加积分路由keypublic static final String CG_BUYING_FINISHADDPOINT_KEY = "finishaddpoint";
/*** 交换机配置* @return the exchange*/@Bean(EX_BUYING_ADDPOINTUSER)public Exchange EX_BUYING_ADDPOINTUSER() {return ExchangeBuilder.directExchange(EX_BUYING_ADDPOINTUSER).durable(true).build();}//声明队列@Bean(CG_BUYING_FINISHADDPOINT)public Queue QUEUE_CG_BUYING_FINISHADDPOINT() {Queue queue = new Queue(CG_BUYING_FINISHADDPOINT);return queue;}//声明队列@Bean(CG_BUYING_ADDPOINT)public Queue QUEUE_CG_BUYING_ADDPOINT() {Queue queue = new Queue(CG_BUYING_ADDPOINT);return queue;}/*** 绑定队列到交换机 .* @param queue the queue* @param exchange the exchange* @return the binding*/@Beanpublic Binding BINDING_QUEUE_FINISHADDPOINT(@Qualifier(CG_BUYING_FINISHADDPOINT) Queue queue, @Qualifier(EX_BUYING_ADDPOINTUSER) Exchange exchange) {return BindingBuilder.bind(queue).to(exchange).with(CG_BUYING_FINISHADDPOINT_KEY).noargs();}@Beanpublic Binding BINDING_QUEUE_ADDPOINT(@Qualifier(CG_BUYING_ADDPOINT) Queue queue, @Qualifier(EX_BUYING_ADDPOINTUSER) Exchange exchange) {return BindingBuilder.bind(queue).to(exchange).with(CG_BUYING_ADDPOINT_KEY).noargs();}
}
二、 订单服务添加任务并发送
步骤1: 修改添加订单方法
当添加订单的时候,添加任务表中相关数据, 代码如下:
//增加任务表记录
Task task = new Task();
task.setCreateTime(new Date());
task.setUpdateTime(new Date());
task.setMqExchange(RabbitMQConfig.EX_BUYING_ADDPOINTURSE);
task.setMqRoutingkey(RabbitMQConfig.CG_BUYING_ADDPOINT_KEY);
Map map = new HashMap();
map.put("userName",order.getUsername());
map.put("orderId",order.getId());
map.put("point",order.getPayMoney());
task.setRequestBody(JSON.toJSONString(map));
taskMapper.insertSelective(task);
步骤2: 定时扫描任务表最新数据
订单服务新增定时任务类,获取小于系统当前时间的所有任务数据
(1) 修改订单服务启动类,添加开启定时任务注解
@EnableScheduling
(2) 定义定时任务类
查询最新数据
更新taskMapper新增方法,查询所有小于系统当前时间的数据
public interface TaskMapper extends Mapper<Task> {
@Select("SELECT * from tb_task WHERE update_time<#{currentTime}")@Results({@Result(column = "create_time",property = "createTime"),@Result(column = "update_time",property = "updateTime"),@Result(column = "delete_time",property = "deleteTime"),@Result(column = "task_type",property = "taskType"),@Result(column = "mq_exchange",property = "mqExchange"),@Result(column = "mq_routingkey",property = "mqRoutingkey"),@Result(column = "request_body",property = "requestBody"),@Result(column = "status",property = "status"),@Result(column = "errormsg",property = "errormsg")})List<Task> findTaskLessTanCurrentTime(Date currentTime);
}
任务类实现
@Component
public class QueryPointTask {
@Autowiredprivate RabbitTemplate rabbitTemplate;
@Autowiredprivate TaskMapper taskMapper;
@Scheduled(cron = "0 0/2 * * * ?")public void queryTask(){
//1.获取小于系统当前时间数据List<Task> taskList = taskMapper.findTaskLessTanCurrentTime(new Date());
if (taskList!=null && taskList.size()>0){//将任务数据发送到消息队列for (Task task : taskList) {rabbitTemplate.convertAndSend(RabbitMQConfig.EX_BUYING_ADDPOINTURSE,RabbitMQConfig.CG_BUYING_ADDPOINT_KEY, JSON.toJSONString(task));}}}
}
三、 用户服务更改积分
步骤1: 添加rabbitmq配置类(与订单服务相同)
@Configuration
public class RabbitMQConfig {//添加积分任务交换机public static final String EX_BUYING_ADDPOINTURSE = "ex_buying_addpointurse";
//添加积分消息队列public static final String CG_BUYING_ADDPOINT = "cg_buying_addpoint";
//完成添加积分消息队列public static final String CG_BUYING_FINISHADDPOINT = "cg_buying_finishaddpoint";
//添加积分路由keypublic static final String CG_BUYING_ADDPOINT_KEY = "addpoint";
//完成添加积分路由keypublic static final String CG_BUYING_FINISHADDPOINT_KEY = "finishaddpoint";
/*** 交换机配置* @return the exchange*/@Bean(EX_BUYING_ADDPOINTURSE)public Exchange EX_DECLARE() {return ExchangeBuilder.directExchange(EX_BUYING_ADDPOINTURSE).durable(true).build();}//声明队列@Bean(CG_BUYING_FINISHADDPOINT)public Queue QUEUE_CG_BUYING_FINISHADDPOINT() {Queue queue = new Queue(CG_BUYING_FINISHADDPOINT);return queue;}//声明队列@Bean(CG_BUYING_ADDPOINT)public Queue QUEUE_CG_BUYING_ADDPOINT() {Queue queue = new Queue(CG_BUYING_ADDPOINT);return queue;}/*** 绑定队列到交换机 .* @param queue the queue* @param exchange the exchange* @return the binding*/@Beanpublic Binding BINDING_QUEUE_FINISHADDPOINT(@Qualifier(CG_BUYING_FINISHADDPOINT) Queue queue, @Qualifier(EX_BUYING_ADDPOINTURSE) Exchange exchange) {return BindingBuilder.bind(queue).to(exchange).with(CG_BUYING_FINISHADDPOINT_KEY).noargs();}@Beanpublic Binding BINDING_QUEUE_ADDPOINT(@Qualifier(CG_BUYING_ADDPOINT) Queue queue, @Qualifier(EX_BUYING_ADDPOINTURSE) Exchange exchange) {return BindingBuilder.bind(queue).to(exchange).with(CG_BUYING_ADDPOINT_KEY).noargs();}
}
步骤2: 定义消息监听类:
@Component
public class AddPointListener {
@Autowiredprivate UserService userService;
@Autowiredprivate RedisTemplate redisTemplate;
@Autowiredprivate RabbitTemplate rabbitTemplate;
@RabbitListener(queues = RabbitMQConfig.CG_BUYING_ADDPOINT)public void receiveMessage(String message){
Task task = JSON.parseObject(message, Task.class);
if (task == null || StringUtils.isEmpty(task.getRequestBody())){return;}
//判断redis中是否存在内容Object value = redisTemplate.boundValueOps(task.getId()).get();if (value != null){return;}
//更新用户积分int result = userService.updateUserPoints(task);
if (result<=0){return;}
//返回通知
rabbitTemplate.convertAndSend(RabbitMQConfig.EX_BUYING_ADDPOINTURSE,RabbitMQConfig.CG_BUYING_FINISHADDPOINT_KEY,JSON.toJSONString(task));
}
}
步骤3: 定义修改用户积分实现
实现思路:
1)判断当前订单是否操作过
2)将任务存入redis
3)修改用户积分
4)添加积分日志表记录
5)删除redis中记录
@Autowired
private PointLogMapper pointLogMapper;
/*** 修改用户积分* @param task* @return*/@Override@Transactionalpublic int updateUserPoints(Task task) {Map info = JSON.parseObject(task.getRequestBody(), Map.class);String userName = info.get("userName").toString();String orderId = info.get("orderId").toString();int point = (int) info.get("point");
//判断当前订单是否操作过PointLog pointLog = pointLogMapper.findLogInfoByOrderId(orderId);if (pointLog != null){return 0;}
//将任务存入redisredisTemplate.boundValueOps(task.getId()).set("exist",1,TimeUnit.MINUTES);
//修改用户积分int result = userMapper.updateUserPoint(userName, point);if (result<=0){return result;}
//添加积分日志表记录pointLog = new PointLog();pointLog.setOrderId(orderId);pointLog.setPoint(point);pointLog.setUserId(userName);result = pointLogMapper.insertSelective(pointLog);if (result<=0){return result;}
//删除redis中的记录redisTemplate.delete(task.getId());
return 1;}
步骤4: 定义根据订单id查询积分日志表
定义PointLogMapper,实现根据订单id查询:
public interface PointLogMapper extends Mapper<PointLog> {
@Select("select * from tb_point_log where order_id=#{orderId}")PointLog findLogInfoByOrderId(@Param("orderId") String orderId);
}
四、 订单服务删除原任务
步骤1: 定义监听类
在订单服务中定义监听类,用于监听队列,如果队列中有消息,则删除原任务防止消息重复发送,并对任务信息进行记录
@Component
public class DelTaskListener {
@Autowiredprivate TaskService taskService;
@RabbitListener(queues = RabbitMQConfig.CG_BUYING_FINISHADDPOINT)public void receiveMessage(String message){
Task task = JSON.parseObject(message, Task.class);
taskService.delTask(task);}
}
步骤: 定义任务service
public interface TaskService {
void delTask(Task task);
}
@Service
@Transactional
public class TaskServiceImpl implements TaskService {
@Autowiredprivate TaskMapper taskMapper;
@Autowiredprivate TaskHisMapper taskHisMapper;
@Overridepublic void delTask(Task task) {//1. 设置删除时间task.setDeleteTime(new Date());Long id = task.getId();task.setId(null);
//bean复制TaskHis taskHis = new TaskHis();BeanUtils.copyProperties(task,taskHis);
//记录任务信息taskHisMapper.insertSelective(taskHis);
//删除原任务task.setId(id);taskMapper.deleteByPrimaryKey(task);}
}
相关文章:

电商项目-分布式事务(四)基于消息队列实现分布式事务
基于消息队列实现分布式事务,实现消息最终一致性 如何基于消息队列实现分布式事务? 通过消息队列实现分布式事务的话,可以保证当前数据的最终一致性。实现思路:将大的分布式事务,进行拆分,拆分成若干个小…...

g++ -> make -> cmake(草稿)
1 Windows上安装mingw 2 构建一个 c 项目 3 g 编译 4 make 编译 5 cmake 编译...

JSON常用的工具方法
前言: 在日常开发中,JSON 数据的处理是常见的需求。无论是数据转换、格式化还是与其他格式的互转,掌握一些常用的工具方法可以大大提高开发效率。本文将介绍一些实用的 JSON 操作方法,帮助你快速上手。 JSON常用的工具方法 1.json字符串转换…...

【Kubernetes Pod间通信-第2篇】使用BGP实现Pod到Pod的通信
Kubernetes中Pod间的通信 本系列文章共3篇: 【Kubernetes Pod间通信-第1篇】在单个子网中使用underlay网络实现Pod到Pod的通信【Kubernetes Pod间通信-第2篇】使用BGP实现Pod到Pod的通信(本文介绍)【Kubernetes Pod间通信-第3篇】Kubernetes中Pod与ClusterIP服务之间的通信…...

[权限提升] Windows 提权 维持 — 系统错误配置提权 - Trusted Service Paths 提权
关注这个专栏的其他相关笔记:[内网安全] 内网渗透 - 学习手册-CSDN博客 0x01:Trusted Service Paths 提权原理 Windows 的服务通常都是以 System 权限运行的,所以系统在解析服务的可执行文件路径中的空格的时候也会以 System 权限进行解析&a…...

8. k8s二进制集群之Kubectl部署
创建kubectl证书请求文件生成admin证书文件复制admin证书到指定目录生成kubeconfig配置文件接下来完成kubectl配置文件的角色绑定【扩展】kubectl命令补全操作继续上一篇文章《k8s二进制集群之Kube ApiServer部署》下面介绍一下k8s中的命令行管理工具kubectl。 通过kubectl可以…...

初学 Xvisor 之理解并跑通 Demo
官网:https://www.xhypervisor.org/ quick-start 文档:https://github.com/xvisor/xvisor/blob/master/docs/riscv/riscv64-qemu.txt 零、Xvisor 介绍 下面这部分是 Xvisor 官方的介绍 Xvisor 是一款开源的 Type-1 虚拟机管理程序,旨在提供一…...

深度内容运营与开源AI智能名片2+1链动模式S2B2C商城小程序在打造种草社区中的应用研究
摘要:移动互联网的迅猛发展极大地改变了消费者的购物行为和消费习惯,传统的购物体验已难以满足用户日益增长的个性化需求。在这种背景下,深度内容运营和实时互动成为提升用户购物体验、影响用户购物行为的重要手段。同时,开源AI智…...

RNN/LSTM/GRU 学习笔记
文章目录 RNN/LSTM/GRU一、RNN1、为何引入RNN?2、RNN的基本结构3、各种形式的RNN及其应用4、RNN的缺陷5、如何应对RNN的缺陷?6、BPTT和BP的区别 二、LSTM1、LSTM 简介2、LSTM如何缓解梯度消失与梯度爆炸? 三、GRU四、参考文献 RNN/LSTM/GRU …...

音频录制一般在什么情况下会选择保存为PCM?什么情况会选择保存为WAV?
在音频开发中,选择保存为 PCM 或 WAV 格式取决于具体的应用场景和需求。以下是两种格式的特点以及适用场景的分析: PCM 格式 特点: 原始音频数据: PCM 是未压缩的原始音频数据,没有任何文件头或元数据。数据直接以二进…...

C#常用744单词
1.visual 可见的 2.studio 工作室 3.dot 点 4.net 网 5.harp 尖端的,锋利的。 6.amework 骨架,构架,框架 7.beta 测试版,试用版 8.XML(全称:eXtensible Markup Language)…...

如何理解算法的正确性?
循环不变式(Loop Invariant) 是算法设计和程序验证中的一个核心概念,用于证明循环的正确性。它是在循环的每次迭代开始和结束时均保持为真的一种条件或性质,帮助开发者确保循环按预期工作,最终达到目标状态。 循环不变…...

蓝桥杯试题:排序
一、问题描述 给定 nn 个正整数 a1,a2,…,ana1,a2,…,an,你可以将它们任意排序。现要将这 nn 个数字连接成一排,即令相邻数字收尾相接,组成一个数。问,这个数最大可以是多少。 输入格式 第一行输入一个正整数 nnÿ…...

实验十一 Servlet(二)
实验十一 Servlet(二) 【实验目的】 1.了解Servlet运行原理 2.掌握Servlet实现方式 【实验内容】 改造实验10,引入数据库,创建用户表,包括用户名和密码:客户端通过login.jsp发出登录请求,请求…...

第五天 初步了解ArkTS和ArkUI
初步了解ArkTS和ArkUI,可以从以下几个方面进行概述: 一、ArkTS简介 定义与关系: ArkTS是HarmonyOS(鸿蒙系统)优选的主力应用开发语言。它基于TypeScript(TS)进行扩展,兼容TS的所有特…...

java中的锁面试题
1、多线程中 synchronized 锁升级的原理是什么? synchronized 是JVM层面的锁,是 Java 关键字,通过 monitor 对象来完成,synchronized 的实现涉及到锁的升级,具体为无锁、偏向锁、自旋锁、重量级锁 synchronized 锁升级…...

ES6 变量解构赋值总结
1. 数组的解构赋值 1.1 基本用法 // 基本数组解构 const [a, b, c] [1, 2, 3]; console.log(a); // 1 console.log(b); // 2 console.log(c); // 3// 跳过某些值 const [x, , y] [1, 2, 3]; console.log(x); // 1 console.log(y); // 3// 解构剩余元素 const [first, ...re…...

知识蒸馏教程 Knowledge Distillation Tutorial
来自于:Knowledge Distillation Tutorial 将大模型蒸馏为小模型,可以节省计算资源,加快推理过程,更高效的运行。 使用CIFAR-10数据集 import torch import torch.nn as nn import torch.optim as optim import torchvision.tran…...

DeepSeek各版本说明与优缺点分析
DeepSeek各版本说明与优缺点分析 DeepSeek是最近人工智能领域备受瞩目的一个语言模型系列,其在不同版本的发布过程中,逐步加强了对多种任务的处理能力。本文将详细介绍DeepSeek的各版本,从版本的发布时间、特点、优势以及不足之处࿰…...

java进阶专栏的学习指南
学习指南 java类和对象java内部类和常用类javaIO流 java类和对象 类和对象 java内部类和常用类 java内部类精讲Object类包装类的认识String类、BigDecimal类初探Date类、Calendar类、SimpleDateFormat类的认识java Random类、File类、System类初识 javaIO流 java IO流【…...

kamailio-osp模块
该文档详细讲解了如何在Kamailio中配置和使用OSP模块(Open Settlement Protocol Module),以实现基于ETSI标准的安全多边对等互联(Secure Multi-Lateral Peering)。以下是核心内容的总结: 1. 模块功能 OSP模…...

【TensorFlow】T1:实现mnist手写数字识别
🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 1、设置GPU import tensorflow as tf gpus tf.config.list_physical_devices("GPU")if gpus:gpu0 gpus[0]tf.config.experimental.set_memory_g…...

Rapidjson 实战
Rapidjson 是一款 C 的 json 库. 支持处理 json 格式的文档. 其设计风格是头文件库, 包含头文件即可使用, 小巧轻便并且性能强悍. 本文结合样例来介绍 Rapidjson 一些常见的用法. 环境要求 有如何的几种方法可以将 Rapidjson 集成到您的项目中. Vcpkg安装: 使用 vcpkg instal…...

【React】受控组件和非受控组件
目录 受控组件非受控组件基于ref获取DOM元素1、在标签中使用2、在组件中使用 受控组件 表单元素的状态(值)由 React 组件的 state 完全控制。组件的 state 保存了表单元素的值,并且每次用户输入时,React 通过事件处理程序来更新 …...

Ollama+deepseek+Docker+Open WebUI实现与AI聊天
1、下载并安装Ollama 官方网址:Ollama 安装好后,在命令行输入, ollama --version 返回以下信息,则表明安装成功, 2、 下载AI大模型 这里以deepseek-r1:1.5b模型为例, 在命令行中,执行&…...

DEEPSEKK GPT等AI体的出现如何重构工厂数字化架构:从设备控制到ERP MES系统的全面优化
随着深度学习(DeepSeek)、GPT等先进AI技术的出现,工厂的数字化架构正在经历前所未有的变革。AI的强大处理能力、预测能力和自动化决策支持,将大幅度提升生产效率、设备管理、资源调度以及产品质量管理。本文将探讨AI体(…...

阿莱(arri)mxf文件变0字节的恢复方法
阿莱(arri)是专业级的影视产品软硬件供应商,很多影视作品都是使用阿莱(arri)的设备拍摄出来的。总体上来讲阿莱(arri)的文件格式有mov和mxf两种,这次恢复的是阿莱(arri)的mxf,机型是arri mini,素材保存在一个8t的硬盘上,使用的是e…...

初识 Node.js
在当今快速发展的互联网技术领域,Node.js 已经成为了一个非常流行且强大的平台。无论是构建高性能的网络应用、实时协作工具还是微服务架构,Node.js 都展示了其独特的优势。本文将带您走进 Node.js 的世界,了解它的基本概念、核心特性以及如何…...

debug-vscode调试方法
debug - vscode gdb调试指南 文章目录 debug - vscode gdb调试指南前言一、调试代码二、命令查看main反汇编查看寄存器打印某个变量打印寄存器,如pc打印当前函数栈信息(当前执行位置)打印程序栈局部变量x命令的语法如下所示:打印某…...

Cypher进阶(函数、索引)
文章目录 Cypher进阶Aggregationcount()函数统计函数collect()函数 unwindforeachmergeunionload csvcall 函数断言函数all()any()~~exists()~~is not nullnone()single() 标量函数coalesce()startNode()/endNode()id()length()size() 列表函数nodes()keys()range()reduce() 数…...