国产时序数据库IotDB安装、与SpringBoot集成 您所在的位置:网站首页 音乐人黄渤 国产时序数据库IotDB安装、与SpringBoot集成

国产时序数据库IotDB安装、与SpringBoot集成

2022-11-14 20:07| 来源: 网络整理| 查看: 265

一.简介: 本文将完成一个真实业务中的设备上报数据的一个例子,完整的展示后台服务接收到设备上报的数据后,将数据添加到时序数据库,并且将数据查询出来的一个例子。本文所有代码已经上传GitHub:https://github.com/Tom-shushu/work-study 下的 iotdb-demo 下。 IoTDB 是针对时间序列数据收集、存储与分析一体化的数据管理引擎。它具有体量轻、性能高、易使用的特点,完美对接 Hadoop 与 Spark 生态,适用于工业物联网应用中海量时间序列数据高速写入和复杂分析查询的需求。 我的理解:它就是一个树形结构的数据库可以很灵活的查询各个级下面的数据,因为它特殊的数据结构也使得它的查询效率会更高一些。 二.Docker安装IotDB: 1.拉取镜像(使用0.13,在使用的过程中0.14在查询时出现了问题) docker pull apache/iotdb:0.13.1-node 2.创建数据文件和日志的 docker 挂载目录 (docker volume) docker volume create mydata docker volume create mylogs 3.直接运行镜像 docker run --name iotdb -p 6667:6667 -v mydata:/iotdb/data -v mylogs:/iotdb/logs -d apache/iotdb:0.13.1-node /iotdb/bin/start-server.sh 4.进入镜像并且登录IotDB docker exec -it iotdb /bin/bash /iotdb/sbin/start-cli.sh -h localhost -p 6667 -u root -pw root

国产时序数据库IotDB安装、与SpringBoot集成 这样就算安装完成,然后打开服务器6667安全组 三.IotDB与SpringBoot集成 1.引入必要的依赖      org.apache.iotdbgroupId> iotdb-sessionartifactId> 0.14.0-preview1version> dependency> cn.hutoolgroupId> hutool-allartifactId> 5.6.3version> dependency> com.alibabagroupId> fastjsonartifactId> 1.2.83version> dependency> org.springframework.bootgroupId> spring-boot-starter-webartifactId> dependency> org.projectlombokgroupId> lombokartifactId> trueoptional> dependency> org.springframework.bootgroupId> spring-boot-starter-testartifactId> testscope> org.junit.vintagegroupId> junit-vintage-engineartifactId> exclusion> exclusions> dependency> 2.编写配置类并且封装对应的方法 IotDBSessionConfig package com.zhouhong.iotdbdemo.config; import lombok.extern.log4j.Log4j2; import org.apache.iotdb.rpc.IoTDBConnectionException; import org.apache.iotdb.rpc.StatementExecutionException; import org.apache.iotdb.session.Session; import org.apache.iotdb.session.SessionDataSet; import org.apache.iotdb.session.util.Version; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.write.record.Tablet; import org.apache.iotdb.tsfile.write.schema.MeasurementSchema; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component; import java.rmi.ServerException; import java.util.ArrayList; import java.util.List; /** * description: iotdb 配置工具类(常用部分,如需要可以自行扩展) * 注意:可以不需要创建分组,插入时默认前两个节点名称为分组名称 比如: root.a1eaKSRpRty.CA3013A303A25467 或者 * root.a1eaKSRpRty.CA3013A303A25467.heart 他们的分组都为 root.a1eaKSRpRty * author: zhouhong */ @Log4j2 @Component @Configuration public class IotDBSessionConfig { private static Session session; private static final String LOCAL_HOST = "XXX.XX.XXX.XX"; @Bean public Session getSession() throws IoTDBConnectionException, StatementExecutionException { if (session == null) { log.info("正在连接iotdb......."); session = new Session.Builder().host(LOCAL_HOST).port(6667).username("root").password("root").version(Version.V_0_13).build(); session.open(false); session.setFetchSize(100); log.info("iotdb连接成功~"); // 设置时区 session.setTimeZone("+08:00"); } return session; } /** * description: 带有数据类型的添加操作 - insertRecord没有指定类型 * author: zhouhong * @param * @param deviceId:节点路径如:root.a1eaKSRpRty.CA3013A303A25467 * time:时间戳 * measurementsList:物理量 即:属性 * type:数据类型: BOOLEAN((byte)0), INT32((byte)1),INT64((byte)2),FLOAT((byte)3),DOUBLE((byte)4),TEXT((byte)5),VECTOR((byte)6); * valuesList:属性值 --- 属性必须与属性值一一对应 * @return */ public void insertRecordType(String deviceId, Long time,List measurementsList, TSDataType type,List valuesList) throws StatementExecutionException, IoTDBConnectionException, ServerException { if (measurementsList.size() != valuesList.size()) { throw new ServerException("measurementsList 与 valuesList 值不对应"); } List types = new ArrayList(); measurementsList.forEach(item -> { types.add(type); }); session.insertRecord(deviceId, time, measurementsList, types, valuesList); } /** * description: 带有数据类型的添加操作 - insertRecord没有指定类型 * author: zhouhong * @param deviceId:节点路径如:root.a1eaKSRpRty.CA3013A303A25467 * @param time:时间戳 * @param measurementsList:物理量 即:属性 * @param valuesList:属性值 --- 属性必须与属性值一一对应 * @return */ public void insertRecord(String deviceId, Long time,List measurementsList, List valuesList) throws StatementExecutionException, IoTDBConnectionException, ServerException { if (measurementsList.size() == valuesList.size()) { session.insertRecord(deviceId, time, measurementsList, valuesList); } else { log.error("measurementsList 与 valuesList 值不对应"); } } /** * description: 批量插入 * author: zhouhong */ public void insertRecords(List deviceIdList, List timeList, List> measurementsList, List> valuesList) throws StatementExecutionException, IoTDBConnectionException, ServerException { if (measurementsList.size() == valuesList.size()) { session.insertRecords(deviceIdList, timeList, measurementsList, valuesList); } else { log.error("measurementsList 与 valuesList 值不对应"); } } /** * description: 插入操作 * author: zhouhong * @param deviceId:节点路径如:root.a1eaKSRpRty.CA3013A303A25467 * @param time:时间戳 * @param schemaList: 属性值 + 数据类型 例子: List schemaList = new ArrayList(); schemaList.add(new MeasurementSchema("breath", TSDataType.INT64)); * @param maxRowNumber: * @return */ public void insertTablet(String deviceId, Long time,List schemaList, List valueList,int maxRowNumber) throws StatementExecutionException, IoTDBConnectionException { Tablet tablet = new Tablet(deviceId, schemaList, maxRowNumber); // 向iotdb里面添加数据 int rowIndex = tablet.rowSize++; tablet.addTimestamp(rowIndex, time); for (int i = 0; i < valueList.size(); i++) { tablet.addValue(schemaList.get(i).getMeasurementId(), rowIndex, valueList.get(i)); } if (tablet.rowSize == tablet.getMaxRowNumber()) { session.insertTablet(tablet, true); tablet.reset(); } if (tablet.rowSize != 0) { session.insertTablet(tablet); tablet.reset(); } } /** * description: 根据SQL查询 * author: zhouhong */ public SessionDataSet query(String sql) throws StatementExecutionException, IoTDBConnectionException { return session.executeQueryStatement(sql); } /** * description: 删除分组 如 root.a1eaKSRpRty * author: zhouhong * @param groupName:分组名称 * @return */ public void deleteStorageGroup(String groupName) throws StatementExecutionException, IoTDBConnectionException { session.deleteStorageGroup(groupName); } /** * description: 根据Timeseries删除 如:root.a1eaKSRpRty.CA3013A303A25467.breath (个人理解:为具体的物理量) * author: zhouhong */ public void deleteTimeseries(String timeseries) throws StatementExecutionException, IoTDBConnectionException { session.deleteTimeseries(timeseries); } /** * description: 根据Timeseries批量删除 * author: zhouhong */ public void deleteTimeserieList(List timeseriesList) throws StatementExecutionException, IoTDBConnectionException { session.deleteTimeseries(timeseriesList); } /** * description: 根据分组批量删除 * author: zhouhong */ public void deleteStorageGroupList(List storageGroupList) throws StatementExecutionException, IoTDBConnectionException { session.deleteStorageGroups(storageGroupList); } /** * description: 根据路径和结束时间删除 结束时间之前的所有数据 * author: zhouhong */ public void deleteDataByPathAndEndTime(String path, Long endTime) throws StatementExecutionException, IoTDBConnectionException { session.deleteData(path, endTime); } /** * description: 根据路径集合和结束时间批量删除 结束时间之前的所有数据 * author: zhouhong */ public void deleteDataByPathListAndEndTime(List pathList, Long endTime) throws StatementExecutionException, IoTDBConnectionException { session.deleteData(pathList, endTime); } /** * description: 根据路径集合和时间段批量删除 * author: zhouhong */ public void deleteDataByPathListAndTime(List pathList, Long startTime,Long endTime) throws StatementExecutionException, IoTDBConnectionException { session.deleteData(pathList, startTime, endTime); } } 3.入参 package com.zhouhong.iotdbdemo.model.param; import lombok.Data; /** * description: 入参 * date: 2022/8/15 21:53 * author: zhouhong */ @Data public class IotDbParam { /*** * 产品PK */ private String pk; /*** * 设备号 */ private String sn; /*** * 时间 */ private Long time; /*** * 实时呼吸 */ private String breath; /*** * 实时心率 */ private String heart; /*** * 查询开始时间 */ private String startTime; /*** * 查询结束时间 */ private String endTime; } 4.返回参数 package com.zhouhong.iotdbdemo.model.result; import lombok.Data; /** * description: 返回结果 * date: 2022/8/15 21:56 * author: zhouhong */ @Data public class IotDbResult { /*** * 时间 */ private String time; /*** * 产品PK */ private String pk; /*** * 设备号 */ private String sn; /*** * 实时呼吸 */ private String breath; /*** * 实时心率 */ private String heart; } 5.使用 package com.zhouhong.iotdbdemo.server.impl; import com.zhouhong.iotdbdemo.config.IotDBSessionConfig; import com.zhouhong.iotdbdemo.model.param.IotDbParam; import com.zhouhong.iotdbdemo.model.result.IotDbResult; import com.zhouhong.iotdbdemo.server.IotDbServer; import lombok.extern.log4j.Log4j2; import org.apache.iotdb.rpc.IoTDBConnectionException; import org.apache.iotdb.rpc.StatementExecutionException; import org.apache.iotdb.session.SessionDataSet; import org.apache.iotdb.tsfile.read.common.Field; import org.apache.iotdb.tsfile.read.common.RowRecord; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.rmi.ServerException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * description: iot服务实现类 * date: 2022/8/15 9:43 * author: zhouhong */ @Log4j2 @Service public class IotDbServerImpl implements IotDbServer { @Resource private IotDBSessionConfig iotDBSessionConfig; @Override public void insertData(IotDbParam iotDbParam) throws StatementExecutionException, ServerException, IoTDBConnectionException { // iotDbParam: 模拟设备上报消息 // bizkey: 业务唯一key PK :产品唯一编码 SN:设备唯一编码 String deviceId = "root.bizkey."+ iotDbParam.getPk() + "." + iotDbParam.getSn(); // 将设备上报的数据存入数据库(时序数据库) List measurementsList = new ArrayList(); measurementsList.add("heart"); measurementsList.add("breath"); List valuesList = new ArrayList(); valuesList.add(String.valueOf(iotDbParam.getHeart())); valuesList.add(String.valueOf(iotDbParam.getBreath())); iotDBSessionConfig.insertRecord(deviceId, iotDbParam.getTime(), measurementsList, valuesList); } @Override public List queryDataFromIotDb(IotDbParam iotDbParam) throws Exception { List iotDbResultList = new ArrayList(); if (null != iotDbParam.getPk() && null != iotDbParam.getSn()) { String sql = "select * from root.bizkey."+ iotDbParam.getPk() +"." + iotDbParam.getSn() + " where time >= " + iotDbParam.getStartTime() + " and time < " + iotDbParam.getEndTime(); SessionDataSet sessionDataSet = iotDBSessionConfig.query(sql); List columnNames = sessionDataSet.getColumnNames(); List titleList = new ArrayList(); // 排除Time字段 -- 方便后面后面拼装数据 for (int i = 1; i < columnNames.size(); i++) { String[] temp = columnNames.get(i).split("\\."); titleList.add(temp[temp.length - 1]); } // 封装处理数据 packagingData(iotDbParam, iotDbResultList, sessionDataSet, titleList); } else { log.info("PK或者SN不能为空!!"); } return iotDbResultList; } /** * 封装处理数据 * @param iotDbParam * @param iotDbResultList * @param sessionDataSet * @param titleList * @throws StatementExecutionException * @throws IoTDBConnectionException */ private void packagingData(IotDbParam iotDbParam, List iotDbResultList, SessionDataSet sessionDataSet, List titleList) throws StatementExecutionException, IoTDBConnectionException { int fetchSize = sessionDataSet.getFetchSize(); if (fetchSize > 0) { while (sessionDataSet.hasNext()) { IotDbResult iotDbResult = new IotDbResult(); RowRecord next = sessionDataSet.next(); List fields = next.getFields(); String timeString = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(next.getTimestamp()); iotDbResult.setTime(timeString); Map map = new HashMap(); for (int i = 0; i < fields.size(); i++) { Field field = fields.get(i); // 这里的需要按照类型获取 map.put(titleList.get(i), field.getObjectValue(field.getDataType()).toString()); } iotDbResult.setTime(timeString); iotDbResult.setPk(iotDbParam.getPk()); iotDbResult.setSn(iotDbParam.getSn()); iotDbResult.setHeart(map.get("heart")); iotDbResult.setBreath(map.get("breath")); iotDbResultList.add(iotDbResult); } } } },> 6.控制层 package com.zhouhong.iotdbdemo.controller; import com.zhouhong.iotdbdemo.config.IotDBSessionConfig; import com.zhouhong.iotdbdemo.model.param.IotDbParam; import com.zhouhong.iotdbdemo.response.ResponseData; import com.zhouhong.iotdbdemo.server.IotDbServer; import lombok.extern.log4j.Log4j2; import org.apache.iotdb.rpc.IoTDBConnectionException; import org.apache.iotdb.rpc.StatementExecutionException; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.rmi.ServerException; /** * description: iotdb 控制层 * date: 2022/8/15 21:50 * author: zhouhong */ @Log4j2 @RestController public class IotDbController { @Resource private IotDbServer iotDbServer; @Resource private IotDBSessionConfig iotDBSessionConfig; /** * 插入数据 * @param iotDbParam */ @PostMapping("/api/device/insert") public ResponseData insert(@RequestBody IotDbParam iotDbParam) throws StatementExecutionException, ServerException, IoTDBConnectionException { iotDbServer.insertData(iotDbParam); return ResponseData.success(); } /** * 插入数据 * @param iotDbParam */ @PostMapping("/api/device/queryData") public ResponseData queryDataFromIotDb(@RequestBody IotDbParam iotDbParam) throws Exception { return ResponseData.success(iotDbServer.queryDataFromIotDb(iotDbParam)); } /** * 删除分组 * @return */ @PostMapping("/api/device/deleteGroup") public ResponseData deleteGroup() throws StatementExecutionException, IoTDBConnectionException { iotDBSessionConfig.deleteStorageGroup("root.a1eaKSRpRty"); iotDBSessionConfig.deleteStorageGroup("root.smartretirement"); return ResponseData.success(); } } 四.PostMan测试 1.添加一条记录 接口:localhost:8080/api/device/insert 入参: { "time":1660573444672, "pk":"a1TTQK9TbKT", "sn":"SN202208120945QGJLD", "breath":"17", "heart":"68" }

国产时序数据库IotDB安装、与SpringBoot集成 查看IotDB数据

国产时序数据库IotDB安装、与SpringBoot集成 2.根据SQL查询时间区间记录(其他查询以此类推) 接口:localhost:8080/api/device/queryData 入参: { "pk":"a1TTQK9TbKT", "sn":"SN202208120945QGJLD", "startTime":"2022-08-14 00:00:00", "endTime":"2022-08-16 00:00:00" } 结果: { "success": true, "code": 200, "message": "请求成功", "localizedMsg": "请求成功", "data": [ { "time": "2022-08-15 22:24:04", "pk": "a1TTQK9TbKT", "sn": "SN202208120945QGJLD", "breath": "19.0", "heart": "75.0" }, { "time": "2022-08-15 22:24:04", "pk": "a1TTQK9TbKT", "sn": "SN202208120945QGJLD", "breath": "20.0", "heart": "78.0" }, { "time": "2022-08-15 22:24:04", "pk": "a1TTQK9TbKT", "sn": "SN202208120945QGJLD", "breath": "17.0", "heart": "68.0" } ] }

Original: https://www.cnblogs.com/Tom-shushu/p/16590246.htmlAuthor: Tom-shushuTitle: 国产时序数据库IotDB安装、与SpringBoot集成

相关阅读 Title: 分布式锁的三种实现方式

点赞再看,养成习惯,微信搜索「 小大白日志」关注这个搬砖人。

文章不定期同步公众号,以及各种一线工厂面试问题,我的学习系列笔记。

[En]

The article irregularly synchronizes the official account, as well as a variety of front-line factory interview questions, my study series of notes.

nacos eureka 应用 nacos是阿里巴巴的开源中间件,可以直接启动jar即可用 eureka需要连着springboot项目一起启动才可用 负载均衡 nacos默认提供权重设置功能,调整承载流量压力 无 心跳机制 nacos支持由客户端或服务端发起的健康检查 Eureka是由客户端发起心跳 负载均衡策略 用Ribion 用Ribion dubbo和k8s的集成 支持 不支持 选型建议 希望引入alibaba生态圈;希望在线对服务上下线&在线流量管理 希望引入spring clound生态圈 一致性协议 支持AP+CP任一种实现 AP 动态配置 支持(方便管理所有环境的服务配置) 不支持

C = 一致性,consistency,任何时候的节点数据都是一样的 A = 可用性,avalibility,任何时候的节点请求不管成功失败都必须有回应 p = 容错性,partition tolerence,任何时候的数据丢失都不会影响系统正常运行

任何分布式系统都无法同时满足CAP,只能满足其中两个,大部分IT公司只要求AP保证服务可用,并且在最终实现”C=最终一致性”即可,如何实现C最终一致性:分布式事务、分布式锁

幂等性:多次重复请求/多次重复操作某一资源,产生的结果是一样的;对于数据库而言,幂等性就是多次重复地对数据库进行某一操作,得到的结果的一样的;对于接口而言,在设计的时候需要考虑幂等性,就是多次重复请求某一个接口,从接口处得到的结果是一样的

操作 是否幂等 示例 查询 是 select * from user where name=’afei’ 新增 是 insert into user(userid,name) values(1,’afei’);若userid是主键,那这个sql就是幂等性的,因为只有第一次数据可以被插入,对数据库产生的结果是一样的;若userid不是主键,那这个sql就不是幂等性的,因为可以重复插入,对数据库产生的结果是不一样的

由上可知分布式锁主要用于解决CAP中的’C’数据一致性问题:分布式环境中,可能存在多个 进程竞争同一个资源,这就需要实现多进程间的”互斥锁”,在java中自带有实现 线程间的互斥锁(Synchronized,Reentranlock),但是分布式环境下进程间的互斥需要自己实现,需要把这个”互斥锁”存在公共的地方被多个进程访问到,这样同一时刻只有一个进程能拿到”互斥锁”,进而保证了数据的一致性=’C’,一般可用redis/zookeeper/数据库来实现分布式锁

又分为两种方式:表锁、版本号机制

表锁:创建表。当您想要操作某些资源时,首先将其锁定,即在表中插入这些“资源记录”,并在解锁时将其删除。 [En]

Table lock: create a table. When you want to manipulate certain resources, lock them first, that is, insert these “resource records” into the table, and delete them when unlocked.*

CREATE TABLE order ( //实现分布式锁的表 id int(11) NOT NULL AUTO_INCREMENT, order_no int(11) DEFAULT NULL comment 锁住的订单号资源, PRIMARY KEY (id), unique key unique_order_no(order_no))ENGINE = INNODB

可知order_no为唯一性约束,当想锁住某个orderNo时->先把它插入表中->当有多个相同的order_no提交到数据库,只有一个能成功->想释放锁时,删除该条记录。可以先检查某个order_no是否在表中,不存在则插入,”检查+插入”应该放到同一个事务中:

@Transactional //“检查+插入”应该放到同一个事务中 public boolean addOrder(int orderNo) { if(orderMapper.selectOrder(orderNo)==null){ //检查 //order表不存在该条记录则插入,表示orderNo订单号被锁定 int result = orderMapper.addOrder(orderNo); if(result>0){ return true; } } return false; } public void fun(int orderNo){ if(addOrder(orderNo)) {//拿到分布式锁 //业务处理 ... //处理完删除order表的orderNo记录,表示释放分布式锁 orderMapper.delete(orderNo); } } 版本号机制 CREATE TABLE order ( //实现分布式锁的表 version int(11) NOT NULL, //版本号、 order_no int(11) DEFAULT NULL comment 锁住的订单号资源, )ENGINE = INNODB

假如已存在version=1,orderNo=’N1’的记录,则:

//先获取锁:

select version from order where order_no=’N1′;

//再占用锁:

udpate order ser version=’2′ where order_no=’N1′ and version=’1′;//更新成功则拿到锁

redis可以用【setnx(key,value)+设置expire】实现分布式锁,这是目前比较优的一种解决方案

public boolean fun(Jedis jedis,String key,String value,int expireTime){ Long r=jedis.setnx(key,value);//若key不存在则保存(key,value)并返回1,代表拿到分布式锁;若key已存在则设置失败并返回0,代表分布式锁已被占用 if(r==1){ //拿到分布式锁 //设置锁的过期时间:从当前时点开始经过expireTime(s)之后该key失效,key-value会被删除,代表分布式锁被释放; jedis.expire(key,expireTime); return true;//拿到true可以向下执行业务操作 } return false; }

产生的问题:

如果在【拿到锁,设置锁的过期时间】之间,程序奔溃,则设置锁的过期时间失败,也即释放锁失败,其他进程读取分布式锁失败,造成了死锁,解决:redis2.6.12之后提供一次性完成setnx和expireTime设置操作 //redis2.6.12之后:setnx提供了expireTime参数,可以一步设置过期时间 public boolean fun(Jedis jedis,String key,String value,int expireTime){ String r=jedis.setnx(key,value,"NX","PX",expireTime);//分布式锁已被占用 if("OK".equals(e)){ //拿到分布式锁 //设置过期时间释放分布式锁 jedis.expire(key,expireTime); return true; } } 拿到锁之后,如果进行业务操作时间太长而expireTime设置太短,则会造成原来只有拿到锁才能执行的代码块失效,解决:设置expireTime之后,马上启动一个定时器,在expireTime快要到来之前,用lua原子性脚本删除原锁并重新设置新锁和新的到期时间(删除原key-value,再重新setnx设key-value和expireTime); 在redis集群下,主节点负责写,从节点负责读,由于主从复制是异步的,所以中间有一定的时间差,如果客户A从主节点获取到锁之后,还没来得及同步信息到其他的从节点,主节点就挂了,这时客户B再拿锁就会从 从节点获取到这个把锁,此时两个客户同时获取到了锁,解决:(待讨论) 锁的可重入问题,因为setnx是保证锁唯一的,如果拥有锁的进程想再次获取到该锁,就会失败,解决:当前进程用lua原子脚本把原锁删了,重新设置新锁+新expireTime(删除原key-value,再重新setnx设key-value和expireTime);

首先创建一个持久的父节点,每当客户端想要竞争访问共享资源时,它们都会在父节点下创建一个临时有序的子节点->

[En]

First create a persistent parent node, and whenever clients want to compete for access to shared resources, they will create a temporary ordered child node under the parent node->

不断地会有新的临时有序子节点被创建->

客户端在访问资源时,会检查自己创建的节点的序列号是否最小,如果是最小的,则获取锁,否则会阻止等待->

[En]

When the client accesses the resource, it will check whether the node created by itself has the lowest sequence number, and if it is the smallest, it will acquire the lock, otherwise it will block waiting->

被阻塞等待的节点均会获取到上一个节点,并为上一节点注册watch事件监听节点是否还存在->

等到上一节点使用完共享资源,则会删除自身,进而触发watch事件被下一节点监听到,下一节点重复上面步骤:检查自己序号是否最小……

产生的问题:频繁地创建删除节点,性能不如redis的实现

特点:可以解决’可重入’、’失效死锁’等问题

OK,如果文章哪里有错误或不足,欢迎各位留言。

创作不易,各位的「 三连」是二少创作的最大动力!我们下期见!分布式锁的三种实现方式

Original: https://www.cnblogs.com/mofes/p/15009158.htmlAuthor: 明天喝可乐Title: 分布式锁的三种实现方式

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/160131/

转载文章受原作者版权保护。转载请注明原作者出处!



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有