SpringBoot3集成TDengine自适应裂变存储
前言
首先很遗憾的告诉大家,今天这篇分享要关注才可以看了。原因是穷啊,现在基本都是要人民币玩家了,就比如chatGPT、copilot,这些AI虽然都是可以很好的辅助编码,但是都是要钱。入驻CSDN有些年头了,中间有几年大学毕业,失恋了没有写,沉沦了几年。后面逐渐捡起来,我们之间应该说是互相成就吧,亦师亦友亦笔记。说实话,其实CSDN之前有出一些插件,我很欣慰,也一直在用,其实我一直希望CSDN能出个copilot采用AI辅助就好了。或者国内几大技术论坛能一起搞个也行,其实大家都是有这方面的优势的,至少代码、训练库是足够的。
言归正传,今天要要分享的要是紧接之前的设计:物联网设备流水入库TDengine改造方案,这里是具体的实现过程。这个是TDengine可自动扩展列方案,这个方案实现代码绝对是目前独家,关注我,你值得拥有。
一、整体思路
整体思路:消费信息 》》 数据转换 》》组织sql 》》orm框架自动配备数据源》》执行入库TDengine》》异常处理(扩展的核心)》》DDL执行扩列》》再次执行入库。。。。
这里大家应该可以猜到具体做法了,其实要不是因为这个列不固定,实现起来可简单多了,也可以用超级表,而且性能也会好很多。更重要的是可以用ORM框架,基本不用写啥sql。而且查询结果用实体接受数据,不会出现VARCHAR字段不能正确显示字符串的问题(我就是被这个坑了下)。
其实也可以用flink等消费信息,做入库处理,当然这样处理可就不能用ORM框架了,只能用经典的JDBC。
核心思路:根据设备上报数据,做插入数据转换sql,执行入库处理异常,根据异常做DDL操作,实现自动扩列,最后入库。上报的数据:json串做数据转换,数据值做反射获取类型,转换为对应的扩列sql执行、组织入库sql。
二、实现流程图

我的整体环境:SpringBoot3 + mybatisPlus + 双数据源(mysql、TDengine)+ 集成kafka
消费上游平台放入kafka的信息,然后走以上流程,目标执行入库TDengine。
三、核心代码
这里的整体框架我之前的博文有写,并且是公开独家分享到csdn的gitCode:https://gitcode.net/zwrlj527/data-trans.git
1.引入库
<dependency><groupId>org.springframework.kafka</groupId><artifactId>spring-kafka</artifactId>
</dependency>
2.配置文件
spring:
#kafka配置kafka:#bootstrap-servers: 192.168.200.72:9092,192.168.200.73:9092#bootstrap-servers: 192.168.200.83:9092,192.168.200.84:9092bootstrap-servers: localhost:9092client-id: dc-device-flow-analyzeconsumer:group-id: dc-device-flow-analyze-consumer-groupmax-poll-records: 10#Kafka中没有初始偏移或如果当前偏移在服务器上不再存在时,默认区最新 ,有三个选项 【latest, earliest, none】auto-offset-reset: earliest#是否开启自动提交enable-auto-commit: false#自动提交的时间间隔auto-commit-interval: 1000listener:ack-mode: MANUAL_IMMEDIATEconcurrency: 1 #推荐设置为topic的分区数type: BATCH #开启批量监听#消费topic配置
xiaotian:analyze:device:flow:topic:consumer: device-flow
3.kafka消费监听
package com.xiaotian.datagenius.kafka;import com.xiaotian.datagenius.service.DataTransService;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.support.Acknowledgment;
import org.springframework.stereotype.Component;import java.util.List;/*** 消费者listener** @author zhengwen**/
@Slf4j
@Component
public class KafkaListenConsumer {@Autowiredprivate DataTransService dataTransService;/*** 设备流水listenner** @param records 消费信息* @param ack Ack机制*/@KafkaListener(topics = "${easylinkin.analyze.device.flow.topic.consumer}")public void deviceFlowListen(List<ConsumerRecord> records, Acknowledgment ack) {log.debug("=====设备流水deviceFlowListen消费者接收信息====");try {for (ConsumerRecord record : records) {log.debug("---开启线程解析设备流水数据:{}", record.toString());dataTransService.deviceFlowTransSave(record);}} catch (Exception e) {log.error("----设备流水数据消费者解析数据异常:{}", e.getMessage(), e);} finally {//手动提交偏移量ack.acknowledge();}}}
4.消息具体处理方法(实现)
package com.xiaotian.datagenius.service.impl;import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.xiaotian.datagenius.mapper.tdengine.DeviceFlowRecordMapper;
import com.xiaotian.datagenius.mapper.tdengine.TableOperateMapper;
import com.xiaotian.datagenius.service.DataTransService;
import com.xiaotian.datagenius.utils.TDengineDbUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;import java.util.*;/*** @author zhengwen*/
@Slf4j
@Service
public class DataTransServiceImpl implements DataTransService {/*** 专门记录业务错误日志*/private final static Logger logger = LoggerFactory.getLogger("businessExp");@Autowiredprivate KafkaTemplate kafkaTemplate;@Autowiredprivate TableOperateMapper tableOperateMapper;@Autowiredprivate DeviceFlowRecordMapper deviceFlowRecordMapper;@Overridepublic void deviceFlowTransSave(ConsumerRecord record) {log.debug("----设备流水转换解析存储----");log.debug(String.format("offset = %d, key = %s, value = %s%n \n", record.offset(), record.key(), record.value()));//字段不可控,所以没有实体可言,只能直接sql//先直接执行插入,try异常 -> 如果是报字段不存在 -> 执行校验字段 -> dml创建字段//再执行插入String stableName = "device_flow_mater";String tableName = "device_flow_record";String recordStr = record.value().toString();if (JSONUtil.isTypeJSON(recordStr)) {JSONObject recordJson = JSONUtil.parseObj(recordStr);//初始化语句Map<String, Map<String, String>> columnData = new HashMap<>();String insertSql = initDataInsertSql(recordJson, tableName, columnData);//保存数据saveRecord(recordJson, insertSql, columnData, tableName);} else {logger.error("---设备上报数据推送信息格式异常,无法解析---");}}/*** 初始化数据插入语句** @param recordJson 记录json* @param tableName 表名* @param columnData 字段信息* @return 数据插入语句*/private String initDataInsertSql(JSONObject recordJson, String tableName, Map<String, Map<String, String>> columnData) {//这里先转换成sql的字段、valueStringJoiner columnSj = new StringJoiner(",");StringJoiner valueSj = new StringJoiner(",");String insertSql = transInitInsertSql(tableName, columnSj, valueSj, recordJson, columnData);if (StringUtils.isBlank(insertSql)) {logger.error("---上报数据转插入语句异常,上报数据:{}", JSONUtil.toJsonStr(recordJson));return null;}return insertSql;}/*** 保存记录** @param recordJson 记录json对象* @param insertSql 插入语句* @param columnData 字段信息* @param tableName 普通表或子表*/private void saveRecord(JSONObject recordJson, String insertSql, Map<String, Map<String, String>> columnData, String tableName) {try {//boolean insertRes = SqlRunner.db(DeviceFlowMaterRecord.class).insert(insertSql, '1');int num = deviceFlowRecordMapper.insert(insertSql);} catch (Exception e) {logger.error("Error inserting,{}", e.getMessage());Throwable throwable = e.getCause();String msg = throwable.getMessage();//报缺少字段、字段长度不够if (msg.contains("Invalid column name:") || msg.contains("Value too long for column/tag")) {transAddOrChangeColumnsSql(columnData, tableName, recordJson, insertSql);}}}/*** 转换扩展列** @param columnData 上报数据字段信息map* @param tableName 表名* @param recordJson 上报数据json* @param insertSql 插入语句*/private void transAddOrChangeColumnsSql(Map<String, Map<String, String>> columnData, String tableName, JSONObject recordJson, String insertSql) {String showColumnsSql = "desc " + tableName;List<Map<String, Object>> columnLs = tableOperateMapper.operateSql(showColumnsSql);if (CollectionUtil.isNotEmpty(columnLs)) {//StringBuffer sbf = new StringBuffer();//sbf.append("ALTER TABLE ").append(tableName).append(" ADD COLUMN ");Map<String, Map<String, Object>> tableColumns = new HashMap<>();columnLs.stream().forEach(c -> {Object byBufferObj = c.get("field");//获取字段String field = TDengineDbUtil.getColumnInfoBy(byBufferObj);tableColumns.put(field, c);});columnData.entrySet().forEach(c -> {String key = c.getKey();Map<String, String> columnMp = c.getValue();String length = columnMp.get("length");if (tableColumns.containsKey(key)) {//包含字段,比较数据类型长度Map<String, Object> tcMp = tableColumns.get(key);Object byBufferObj = tcMp.get("length");//获取字段长度String dbLength = TDengineDbUtil.getColumnInfoBy(byBufferObj);if (dbLength != null) {if (Integer.parseInt(length) > Integer.parseInt(dbLength)) {String changeColumnSql = TDengineDbUtil.getColumnChangeSql(tableName,length,key);tableOperateMapper.operateSql(changeColumnSql);}}} else {//不包含需要执行增加字段String addColumnSql = TDengineDbUtil.getColumnAddSql(tableName,length,key);tableOperateMapper.operateSql(addColumnSql);}});//复调存储saveRecord(recordJson, insertSql, columnData, tableName);}}/*** 转换初始化插入语句sql** @param tableName 表名* @param columnSj 字段字符串* @param valueSj 值字符串* @param recordJson 上报数据json* @param columnData 字段Map* @return 插入语句sql*/private String transInitInsertSql(String tableName, StringJoiner columnSj, StringJoinervalueSj, JSONObject recordJson, Map<String, Map<String, String>> columnData) {StringBuffer sb = new StringBuffer();//子表不能扩展列,所以超级表思路走不通sb.append("insert into ").append(tableName);if (!JSONUtil.isNull(recordJson)) {JSONObject tmpRecordJson = recordJson;JSONObject dataJson = tmpRecordJson.getJSONObject("data");Date collectTime = tmpRecordJson.getDate("collectTime");tmpRecordJson.remove("data");tmpRecordJson.entrySet().forEach(entry -> {//TODO 这里要设置调整下数据库区分大小写后去掉//String key = entry.getKey().toLowerCase();String key = entry.getKey();columnSj.add("`" + key + "`");Object val = entry.getValue();//TODO 校验字符串类型处理sqlint length = 5;if (val != null) {//TODO 几个时间字段传的是long,是转时间类型,还是改字段为字符串?String valStr = TDengineDbUtil.convertValByKey(val,key);valueSj.add(valStr);length = valStr.length() + 5;} else {valueSj.add(null).add(",");}//TODO 字段数据类型后面要优化处理Map<String, String> columnMp = TDengineDbUtil.checkColumnType(key, val, length);columnData.put(key, columnMp);});if (!JSONUtil.isNull(dataJson)) {dataJson.entrySet().forEach(entry -> {//TODO 这里要设置调整下数据库区分大小写后去掉String key = entry.getKey();columnSj.add("`" + key + "`");Object val = entry.getValue();int length = 3;if (val != null) {//TODO 几个时间字段传的是long,是转时间类型,还是改字段为字符串?String valStr = TDengineDbUtil.convertValByKey(val,key);valueSj.add(valStr);length = valStr.length() + 1;} else {valueSj.add(null).add(",");}//TODO 字段数据类型后面要优化处理Map<String, String> columnMp = TDengineDbUtil.checkColumnType(key, val, length);columnData.put(key, columnMp);});}//Tags//sb.append(" TAGS (").append(dataJson).append(",").append(deviceUnitCode).append(",").append(deviceCode).append(") ");//sb.append(" TAGS ('").append(JSONUtil.toJsonStr(dataJson)).append("') ");//ColumnscolumnSj.add("`data_ts`");sb.append("(").append(columnSj.toString()).append(") ");//Values//valueSj.add("'" + DateUtil.format(collectTime, DatePattern.NORM_DATETIME_MS_FORMAT) + "'");//主键应该是时间,不能是设备上报数据的时间,因为设备上报数据万一相同就更新了valueSj.add("NOW");sb.append(" VALUES (").append(valueSj.toString()).append(")");logger.debug("----插入语句sql:", sb.toString());return sb.toString();}return null;}
}
5.工具类
package com.xiaotian.datagenius.utils;import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import io.micrometer.core.instrument.util.TimeUtils;
import lombok.extern.slf4j.Slf4j;import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;/*** TDengine数据库工具类** @author zhengwen*/
@Slf4j
public class TDengineDbUtil {/*** orm框架执行ddl语句返回的字段是byte数组处理** @param byBufferObj byte数组object对象* @return*/public static String getColumnInfoBy(Object byBufferObj) {try {if (byBufferObj instanceof byte[]) {byte[] bytes = (byte[]) byBufferObj;ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.write(bytes);oos.flush();String strRead = new String(bytes);oos.close();bos.close();return strRead;}} catch (IOException e) {log.error("----字段异常:{}", e.getMessage());}return null;}/*** 校验字段类型返回字段信息** @param key 字段* @param val 值* @param length 长度* @return 字段信息*/public static Map<String, String> checkColumnType(String key, Object val, int length) {Map<String, String> columnMp = new HashMap<>();columnMp.put("type", "String");columnMp.put("length", String.valueOf(length));return columnMp;}/*** @param tableName* @param length* @param key* @return*/public static String getColumnAddSql(String tableName, String length, String key) {String beforeSql = "ALTER TABLE " + tableName + " ADD COLUMN ";//TODO 处理字段类型String addColumnSql = beforeSql + "`" + key + "` NCHAR(" + Integer.parseInt(length) + ")";return addColumnSql;}/*** @param tableName* @param length* @param key* @return*/public static String getColumnChangeSql(String tableName, String length, String key) {String changeLengthSql = "ALTER TABLE " + tableName + " MODIFY COLUMN ";//TODO 处理字段类型String changeColumnSql = changeLengthSql + "`" + key + "` NCHAR(" + length + ")";return changeColumnSql;}/*** 根据字段、字段值对插入sql的字段值做处理** @param val 字段原始值* @param key 字段* @return 字段转换后的值*/public static String convertValByKey(Object val, String key) {//其他全部当字符串处理String valStr = "'" + val.toString() + "'";//TODO 根据字段处理转换后的字段值,这里暂时对几个时间字段做特殊处理if (key.equals("collectTime") || key.equals("createTime") || key.equals("storageTime")) {if (val instanceof Long){LocalDateTime localDateTime = LocalDateTimeUtil.of(Long.parseLong(val.toString()));valStr = "'" + DateUtil.format(localDateTime,DatePattern.NORM_DATETIME_MS_PATTERN) + "'";}if (val instanceof Integer){LocalDateTime localDateTime = LocalDateTimeUtil.of(Long.parseLong(val.toString() + 100));valStr = "'" + DateUtil.format(localDateTime,DatePattern.NORM_DATETIME_MS_PATTERN) + "'";}}return valStr;}
}
6.Mapper的重要方法
TableOperateMapper
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.xiaotian.datagenius.mapper.tdengine.TableOperateMapper"><select id="operateSql" resultType="java.util.Map">${sql}</select>
</mapper>
DeviceFlowRecordMapper
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xiaotian.datagenius.mapper.tdengine.DeviceFlowRecordMapper"><!-- 通用查询映射结果 --><resultMap id="BaseResultMap" type="com.xiaotian.datagenius.entity.DeviceFlowRecord"><id column="data_ts" property="dataTs" /><result column="deviceUnitCode" property="deviceUnitCode" /><result column="deviceUnitName" property="deviceUnitName" /><result column="deviceCode" property="deviceCode" /><result column="deviceName" property="deviceName" /><result column="deviceTypeName" property="deviceTypeName" /><result column="collectTime" property="collectTime" /><result column="createTime" property="createTime" /><result column="storageTime" property="storageTime" /><result column="projectId" property="projectId" /><result column="companyId" property="companyId" /><result column="data" property="data" /><result column="showMessage" property="showMessage" /></resultMap><insert id="insert">${sql}</insert><select id="selectPageMap" resultType="java.util.Map">${sql}</select><select id="selectPageBy" parameterType="com.easylinkin.datagenius.vo.DeviceFlowRecordVo" resultType="java.util.Map">select r.*from device_flow_record rwhere 1 = 1<if test="param2 != null"><if test="param2.deviceCode != null and param2.deviceCode != ''">and r.`deviceCode` = #{param2.deviceCode}</if><if test="param2.startTime != null"><![CDATA[ and r.`collectTime` >= #{param2.startTime} ]]></if><if test="param2.endTime != null"><![CDATA[ and r.`collectTime` <= #{param2.endTime} ]]></if></if>order by r.`data_ts` desc</select>
</mapper>
核心点就以上这些位置了,大家自行体会。
总结
- TDengine还不错,官方有交流群,群里也有技术支持,不过肯定不是每一个问题都有回复
- 各方面都在支持它,它的优化空间还很多,我们用开源实际也是在帮忙测试。就从开年到现在我就整这玩意,刚开始是3.0.2.3现在都迭代到3.0.2.5了
- ORM框架也在逐步支持,但是官方支持明确跟我说了可能ORM框架会拖慢,影响性能。
- 里面坑还是很多的,我就踩了乱码、返回字节码的问题
就写到这里,希望能帮到大家!!有需要帮助的可以在CSDN发消息我。
相关文章:
SpringBoot3集成TDengine自适应裂变存储
前言 首先很遗憾的告诉大家,今天这篇分享要关注才可以看了。原因是穷啊,现在基本都是要人民币玩家了,就比如chatGPT、copilot,这些AI虽然都是可以很好的辅助编码,但是都是要钱。入驻CSDN有些年头了,中间有几…...
golang alpine 配置gstreamer开发环境
启动容器 sudo docker run -it --name golang -v $PWD:/home/leon -d golang:1.18-alpine3.17tar zxvf x86_64-linux-musl-cross.tgz mv x86_64-linux-musl-cross /usr/local/musl export PATH$PATH:/usr/local/musl/bin/:/usr/local/musl/x86_64-linux-musl/bin 下载gstre…...
SAP ABAP GUI_DOWNLOAD中下载乱码的问题
1 GUI_DOWNLOAD 1.1 问题表现 GUI_DOWNLOAD在应用当中有时会导致输出的文件在某些电脑正常显示,在某些电脑乱码显示。这个固然是由于各个电脑系统配置有差异,但是我们可以在应用该函数时就排除该差异来保证任意台电脑正常显示输出的文件。 如下…...
接口和抽象类
接口(Interface)和抽象类(Abstract Class)是支持抽象类定义的两种机制。 1.抽象类 (1)说明 在Java中被abstract关键字修饰的类称为抽象类,被abstract关键字修饰的方法称为抽象方法,抽象方法只有方法的声明,没有方法体。抽象类是用来捕捉子…...
ES7新特性
1. ES7 新特性 1.1. Array.prototype.includes includes 方法用来检测是否包含某个数组,返回布尔类型值 其他检测包含字符串的方法: indexOf(),返回的是下标值,如果没有则返回-1 1.2 指数操作符 指数…...
【软件测试】资深测试总结的几个自动化测试点,提升跨越一大步......
目录:导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜)前言 自动化的软件测试与…...
GEE:时间序列分析1——认识arraySlice()
本文是记录时间序列分析系列教程的开篇之作,教程由浅入深介绍在GEE平台上进行时间序列分析的方法和代码。本教程会从操作时间序列的基本函数开始讲解,到后续更新会加入一些成熟的时间序列分析方法。随着本教程文章数量增加到一定数量,本专栏会适当涨价。欢迎乐多们订阅。 文…...
【react实战小项目:笔记】用React 16写了个订单页面
视频地址 React 16 实现订单列表及评价功能 简介:React 以其组件化的思想在前端领域大放异彩,但其革命化的前端开发理念对很多 React 初学者来说, 却很难真正理解和应用到真实项目中。本课程面向掌握了 React 基础知识但缺乏实战经验的开发…...
30岁+的人如何进行自我提升和职场规划
今天非常忙一天开了N个会,一堆头疼的事情要解决,一晃就加班到现在,刚打到了的士开始想今天分享点什么。 实在不知道写什么了,突然想起下午部门茶话会小伙伴问的问题:“30岁的人如何进行自我提升和职场规划”。 这是个…...
创建基于Vue2.0开发项目的两种方式
前天开始接触基于Vue2.0的前端项目,实际操作中肯定会遇到一些问题,慢慢摸索和总结。 其实,作为开发一般企事业单位应用的小项目,前端的懂一点HTMLCSSJavaScroptJQueryJson(或者Xml),后端懂一…...
[测试]性能测试
最近遇到一个性能测试的问题,虽然最后确定是一个乌龙问题。这里还是总结一下,看是否有可以从中学到什么。 场景: 月底要出一个新版本。测试人员发现这个新版本在相同的负载的情况下,会有队列使用负荷过高的警告。之前的版本没有。…...
剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
摘要 剑指 Offer 21. 调整数组顺序使奇数位于偶数前面 一、双指针解析 考虑定义双指针 i , j分列数组左右两端,循环执行: 指针 i从左向右寻找偶数;指针 j从右向左寻找奇数;将偶数nums[i]和奇数 nums[j]交换。 可始终保证&…...
实用版ChatBing论文阅读助手教程+新测评
实用版ChatBing论文阅读助手新测评 AI进化(更新)的速度太快了!距离我上次的【Chat嘴硬!基于NewBing的论文调研评测报告】,才四天,它已经进化到快能用的地步了! 这次是我刷B站看到热门推荐&…...
Linux生产者消费模型
1.生产者消费者模型 1.1 为何要使用生产者消费者模型 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接…...
动态网站开发讲课笔记01:网页开发基础
文章目录零、本讲学习目标一、HTML基础(一)HTML简介1、HTML2、HTML语言的基本格式3、<!DOCTYPE>声明4、html标签5、head标签6、body标签7、编写第一个网页8、关于编写HTML文件的工具9、HTML标签概述(1)单标签(2&…...
互联网新时代要到来了(三)什么是ChatGPT?
什么是ChatGPT? tips:资料来自百度百科、openAi、CSDN博主「琦在江湖飘」、Info写作社区、CSDN博主「夕小瑶」等网页资料。 1.什么是ChatGPT? ChatGPT(全名:Chat Generative Pre-trained Transformer),…...
华为OD机试 - 环中最长子串(Python)
环中最长子串 题目 给你一个字符串s,首尾相连成一个环形, 请你在环中找出o字符出现了偶数次最长子字符串的长度. 备注: 1 <= s.lenth <= 5x10^5 s只包含小写英文字母 输入 输入是一个小写字母组成的字符串 输出描述 输出是一个整数 示例一 输入 alolobo输出 6说…...
安全—08day
ApabilitiesapabilitiesLinux Capabilities线程的 capabilitiesPermitted 允许Effective 有效InheritableBoundingAmbient文件的 capabilitiesPermittedInheritableEffective运行 execve() 后 capabilities 的变化案例分析方法一、依次执行如下命令方法二、iptables端口转发方案…...
【看表情包学Linux】进程地址空间 | 区域和页表 | 虚拟地址空间 | 初识写时拷贝
🤣 爆笑教程 👉 《看表情包学Linux》👈 猛戳订阅 🔥 💭 写在前面:本章核心主题为 "进程地址空间",会通过验证 Linux 进程的地址空间来开头,抛出 "同一个值能有不同内…...
响应式编程(Reactive Programming)介绍
什么是响应式编程? 在互联网上有着一大堆糟糕的解释与定义。Wikipedia 一如既往的空泛与理论化。Stackoverflow 的权威答案明显不适合初学者。Reactive Manifesto 看起来是你展示给你公司的项目经理或者老板们看的东西。微软的 Rx terminology"Rx Observables LINQ S…...
19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...
论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...
