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…...
你不知道的美化列表的两种方案-<ul/><ol/>
大家好,我是半夏👴,一个励志更文1000篇沙雕程序员.如果喜欢我的文章,可以关注➕ 点赞 一起学习交流前端,成为更优秀的工程师~ CSS为什么这么难学?一定是你方法不对!!! 只要一杯奶茶,CSS任你学。学透CSS,拒绝切图仔!!! 学透CSS传送门 文章目录 学透CSS传送门前言li…...
2023年浙江理工大学MBA招生考试初试成绩查询及复查的通知
根据往年的情况,2023浙江理工大学MBA考试初试成绩可能将于2月21日下午两点公布,为了广大考生可以及时查询到自己的分数,杭州达立易考教育为大家汇总了信息。 一、成绩查询考生可登录中国研究生招生信息网“全国硕士研究生招生考试初试成绩查询…...
SVNH数据(.mat格式)转为图像(.png)matlab代码
一、获取SVNH数据数据集集地址-http://ufldl.stanford.edu/housenumbers/提供两种格式的数据:1.Format 1,图像形式,压缩包2.Format 2, .mat格式的数据10 classes, 1 for each digit. Digit 1 has label 1, 9 has label 9 and 0 ha…...
【总结】vim教程与详细命令总结,该来的躲不掉啊晕
B站|公众号:啥都会一点的研究生 目录写在前面vim的工作模式普通模式编辑模式命令模式命令大全,最详细(建议收藏)光标的移动插入模式 - 插入/追加文本编辑文本选择文本(可视化模式)可视化模式命令剪切, 复制…...
git基础使用
Git安装 去安装>> 正式开始 进入要管理的目录,执行命令 git init 查看管理目录下的状态 git status 注:新增文件和修改过后的文件都是红色 管理指定文件(红变绿) 指定文件:git add 文件名 当前目录下所有&…...
基于 RANSAC 的地面分割与聚类算法
文章目录 前言 一、算法原理 参考文献 二、代码实现 1.头文件 2.源文件...
JVM内存模型深度剖析与优化
1. Java语言的跨平台特性 2. JVM整体结构及内存模型 堆存放着对象信息每个线程都会分配一块属于自己的内存空间(栈空间) 每个方法都会分配一块内存空间(栈桢),上图 compute()方法 和 main()方法 都会分配到各自的栈桢空…...
软件性能测试定义中文
From Wiki软件性能测试在软件质量保证中,性能测试通常是一种测试实践,用于确定系统在特定工作负载下的响应能力和稳定性方面的表现。它还可以用于调查、测量、验证或验证系统的其他质量 属性,例如可扩展性、可靠性和资源使用。性能测试是性能…...
2023情人节正经性生活调研报告
省时查报告-专业、及时、全面的行研报告库省时查方案-专业、及时、全面的营销策划方案库【免费下载】2023年1月份热门报告合集ChatGPT的发展历程、原理、技术架构及未来方向2023年,如何科学制定年度规划?《底层逻辑》高清配图今天给大家带来丁香医生最新…...
22- 隐马尔科夫HMM (NLP自然语言算法) (算法)
HMM模型 : from hmmlearn.hmm import GaussianHMM model GaussianHMM(n_components3,n_iter100000, covariance_type diag) model.fit(X) 1、马尔科夫链 有向图模型(贝叶斯网络):用有向图表示变量间的依赖关系; 无向图模型&…...
做网站的研究生专业/windows优化大师官方
1、TinyMCE下载 官方下载网址:http://www.tinymce.com/ 简体中文语言包下载:http://www.tinymce.com/i18n/index.php?ctrllang&actdownload&pr_id1 2、下载包中示例 代码: <!DOCTYPE html> <html> <head><titl…...
网站名称写什么/网站流量统计分析的维度包括
问题:求含有n个点的连通图的个数。 解: 考虑DP,$f(n)$表示n个点,每个点都和点1相连,且n个点互相连通的图的个数。 (蓝字非常重要,这个条件有效地避免了重复计算) $g(n)$表示n个点&am…...
西安做网站的公司电话/深圳优化公司哪家好
同步发表:http://blog.hacktons.cn/2017/12/13/shell-func-return/ 背景 通过shell编程,写一些工具批处理的时候,经常需要自定义函数。更复杂点的情况下,可能有需要返回一个值。 由于在shell的世界中,并不像其他编程语…...
wordpress使用置顶文章没用/网络平台的推广方法
PPT文件上呦限制编辑,不知道密码应该怎么去掉? 取消限制编辑在不知道密码的情况下,需要用到工具的帮助【PPT解密大师】快速找回密码_轻松移除使用限制-奥凯丰okfone 选择【解除限制】将PPT文件添加到软件中,点击一下【开始】就可…...
美食网站代做/软文推广多少钱一篇
1. 修改类函数。 场景: 如果要给一个类的所有方法加上计时,并打印出来。demo如下: # -*- coding:utf-8 -*- import time def time_it(fn):"Example of a method decorator"def decorator(*args, **kwargs):t1time.time()ret fn(*a…...
网络 企业网站/互联网广告代理
一、单位的换算 1字节(B)8bit 1KB1024字节 1MB1024KB 1GB1024MB 1TB1024GB 通信单位中 K千 , M 百万 计算机单位中 K2^10 , M 2^20 倍数刚好是1.024的幂 ^ 为次方; /为除 ; *为乘 ; (X/X)为单位 二、计算总线数据传输速率 总线数据传输速率时钟频率(Mhz)/每个总线包…...