ShardingSphere自定义分布式主键生成策略、自定义分片规则

文章目录

    • 主键生成策略源码
      • KeyGenerateAlgorithm
      • 源码入口
      • 实现
      • 扩展 自定义分布式主键生成策略
    • 分片算法
      • ShardingAlgorithm
      • 实现
      • 扩展 自定义分片算法
      • 踩的坑

主键生成策略源码

开发者手册

KeyGenerateAlgorithm

全限定类名org.apache.shardingsphere.sharding.spi.KeyGenerateAlgorithm

分布式主键生成算法,已知实现

配置标识详细说明全限定类名
SNOWFLAKE基于雪花算法的分布式主键生成算法org.apache.shardingsphere.sharding.algorithm.keygen.SnowflakeKeyGenerateAlgorithm
UUID基于 UUID 的分布式主键生成算法org.apache.shardingsphere.sharding.algorithm.keygen.UUIDKeyGenerateAlgorithm
NANOID基于 NanoId 的分布式主键生成算法org.apache.shardingsphere.sharding.nanoid.algorithm.keygen.NanoIdKeyGenerateAlgorithm
COSID基于 CosId 的分布式主键生成算法org.apache.shardingsphere.sharding.cosid.algorithm.keygen.CosIdKeyGenerateAlgorithm
COSID_SNOWFLAKE基于 CosId 的雪花算法分布式主键生成算法org.apache.shardingsphere.sharding.cosid.algorithm.keygen.CosIdSnowflakeKeyGenerateAlgorithm



源码入口

package org.apache.shardingsphere.sharding.factory;/*** Key generate algorithm factory.*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class KeyGenerateAlgorithmFactory {//加载所有的主键生成策略static {ShardingSphereServiceLoader.register(KeyGenerateAlgorithm.class);}/*** 根据配置的主键生成策略,获取一个主键生成算法* 例如:spring.shardingsphere.rules.sharding.key-generators.usercourse_keygen.type=SNOWFLAKE*/public static KeyGenerateAlgorithm newInstance(final AlgorithmConfiguration keyGenerateAlgorithmConfig) {return ShardingSphereAlgorithmFactory.createAlgorithm(keyGenerateAlgorithmConfig, KeyGenerateAlgorithm.class);}/*** 判断是否包含配置的算法*/public static boolean contains(final String keyGenerateAlgorithmType) {return TypedSPIRegistry.findRegisteredService(KeyGenerateAlgorithm.class, keyGenerateAlgorithmType).isPresent();}
}



先来看主键生成策略是如何加载的:ShardingSphereServiceLoader.register(KeyGenerateAlgorithm.class);

public final class ShardingSphereServiceLoader {//线程安全Map,缓存所有主键生成器private static final Map<Class<?>, Collection<Object>> SERVICES = new ConcurrentHashMap<>();// 进入到register()方法中public static void register(final Class<?> serviceInterface) {if (!SERVICES.containsKey(serviceInterface)) {// 调用下方的load()方法SERVICES.put(serviceInterface, load(serviceInterface));}}//使用java的SPI机制加载接口的所有实现类private static <T> Collection<Object> load(final Class<T> serviceInterface) {Collection<Object> result = new LinkedList<>();for (T each : ServiceLoader.load(serviceInterface)) {result.add(each);}return result;}
}



实现

ShardingJDBC是通过SPI机制,加载org.apache.shardingsphere.sharding.spi.KeyGenerateAlgorithm接口的实现类,也就是上方表格中的内容

我们就可以直接在yml配置文件中进行配置分布式主键生成算法



接下来就以SNOWFLAKE雪花算法举例,下方就列举出了几个关键方法

// 实现了KeyGenerateAlgorithm接口
public final class SnowflakeKeyGenerateAlgorithm implements KeyGenerateAlgorithm, InstanceContextAware {// 在init方法中,会把我们yml配置文件中定义的props的配置项,保存在下面方法的形参中,并赋值给props成员属性// 其他地方再用props对象获取我们的配置项@Overridepublic void init(final Properties props) {this.props = props;maxVibrationOffset = getMaxVibrationOffset(props);maxTolerateTimeDifferenceMilliseconds = getMaxTolerateTimeDifferenceMilliseconds(props);}// 实现KeyGenerateAlgorithm接口中的抽象方法generateKey()// 也就是在这个方法中具体生成分布式主键值的@Overridepublic synchronized Long generateKey() {long currentMilliseconds = timeService.getCurrentMillis();if (waitTolerateTimeDifferenceIfNeed(currentMilliseconds)) {currentMilliseconds = timeService.getCurrentMillis();}if (lastMilliseconds == currentMilliseconds) {if (0L == (sequence = (sequence + 1) & SEQUENCE_MASK)) {currentMilliseconds = waitUntilNextTime(currentMilliseconds);}} else {vibrateSequenceOffset();sequence = sequenceOffset;}lastMilliseconds = currentMilliseconds;return ((currentMilliseconds - EPOCH) << TIMESTAMP_LEFT_SHIFT_BITS) | (getWorkerId() << WORKER_ID_LEFT_SHIFT_BITS) | sequence;}// getType() 方法中返回的字符串就是我们上方yml配置文件中type配置项填写的值@Overridepublic String getType() {return "SNOWFLAKE";}
}



其他几个实现类也是一样的格式

在这里插入图片描述



扩展 自定义分布式主键生成策略

package com.hs.sharding.algorithm;import com.google.common.base.Preconditions;
import org.apache.shardingsphere.infra.instance.InstanceContext;
import org.apache.shardingsphere.infra.instance.InstanceContextAware;
import org.apache.shardingsphere.sharding.algorithm.keygen.TimeService;
import org.apache.shardingsphere.sharding.spi.KeyGenerateAlgorithm;import java.util.Calendar;
import java.util.Properties;/*** 改进雪花算法,让他能够 %4 均匀分布。* @auth hs*/
public final class MySnowFlakeAlgorithm implements KeyGenerateAlgorithm, InstanceContextAware {public static final long EPOCH;private static final String MAX_VIBRATION_OFFSET_KEY = "max-vibration-offset";private static final String MAX_TOLERATE_TIME_DIFFERENCE_MILLISECONDS_KEY = "max-tolerate-time-difference-milliseconds";private static final long SEQUENCE_BITS = 12L;private static final long WORKER_ID_BITS = 10L;private static final long SEQUENCE_MASK = (1 << SEQUENCE_BITS) - 1;private static final long WORKER_ID_LEFT_SHIFT_BITS = SEQUENCE_BITS;private static final long TIMESTAMP_LEFT_SHIFT_BITS = WORKER_ID_LEFT_SHIFT_BITS + WORKER_ID_BITS;private static final int DEFAULT_VIBRATION_VALUE = 1;private static final int MAX_TOLERATE_TIME_DIFFERENCE_MILLISECONDS = 10;private static final long DEFAULT_WORKER_ID = 0;private static TimeService timeService = new TimeService();public static void setTimeService(TimeService timeService) {MySnowFlakeAlgorithm.timeService = timeService;}private Properties props;@Overridepublic Properties getProps() {return props;}private int maxVibrationOffset;private int maxTolerateTimeDifferenceMilliseconds;private volatile int sequenceOffset = -1;private volatile long sequence;private volatile long lastMilliseconds;private volatile InstanceContext instanceContext;static {Calendar calendar = Calendar.getInstance();calendar.set(2016, Calendar.NOVEMBER, 1);calendar.set(Calendar.HOUR_OF_DAY, 0);calendar.set(Calendar.MINUTE, 0);calendar.set(Calendar.SECOND, 0);calendar.set(Calendar.MILLISECOND, 0);EPOCH = calendar.getTimeInMillis();}@Overridepublic void init(final Properties props) {this.props = props;maxVibrationOffset = getMaxVibrationOffset(props);maxTolerateTimeDifferenceMilliseconds = getMaxTolerateTimeDifferenceMilliseconds(props);}@Overridepublic void setInstanceContext(final InstanceContext instanceContext) {this.instanceContext = instanceContext;if (null != instanceContext) {instanceContext.generateWorkerId(props);}}private int getMaxVibrationOffset(final Properties props) {int result = Integer.parseInt(props.getOrDefault(MAX_VIBRATION_OFFSET_KEY, DEFAULT_VIBRATION_VALUE).toString());Preconditions.checkArgument(result >= 0 && result <= SEQUENCE_MASK, "Illegal max vibration offset.");return result;}private int getMaxTolerateTimeDifferenceMilliseconds(final Properties props) {return Integer.parseInt(props.getOrDefault(MAX_TOLERATE_TIME_DIFFERENCE_MILLISECONDS_KEY, MAX_TOLERATE_TIME_DIFFERENCE_MILLISECONDS).toString());}@Overridepublic synchronized Long generateKey() {long currentMilliseconds = timeService.getCurrentMillis();if (waitTolerateTimeDifferenceIfNeed(currentMilliseconds)) {currentMilliseconds = timeService.getCurrentMillis();}if (lastMilliseconds == currentMilliseconds) {
//            if (0L == (sequence = (sequence + 1) & SEQUENCE_MASK)) {currentMilliseconds = waitUntilNextTime(currentMilliseconds);
//            }} else {vibrateSequenceOffset();
//            sequence = sequenceOffset;sequence = sequence >= SEQUENCE_MASK ? 0:sequence+1;}lastMilliseconds = currentMilliseconds;return ((currentMilliseconds - EPOCH) << TIMESTAMP_LEFT_SHIFT_BITS) | (getWorkerId() << WORKER_ID_LEFT_SHIFT_BITS) | sequence;}private boolean waitTolerateTimeDifferenceIfNeed(final long currentMilliseconds) {if (lastMilliseconds <= currentMilliseconds) {return false;}long timeDifferenceMilliseconds = lastMilliseconds - currentMilliseconds;Preconditions.checkState(timeDifferenceMilliseconds < maxTolerateTimeDifferenceMilliseconds,"Clock is moving backwards, last time is %d milliseconds, current time is %d milliseconds", lastMilliseconds, currentMilliseconds);try {Thread.sleep(timeDifferenceMilliseconds);} catch (InterruptedException e) {}return true;}private long waitUntilNextTime(final long lastTime) {long result = timeService.getCurrentMillis();while (result <= lastTime) {result = timeService.getCurrentMillis();}return result;}@SuppressWarnings("NonAtomicOperationOnVolatileField")private void vibrateSequenceOffset() {sequenceOffset = sequenceOffset >= maxVibrationOffset ? 0 : sequenceOffset + 1;}private long getWorkerId() {return null == instanceContext ? DEFAULT_WORKER_ID : instanceContext.getWorkerId();}@Overridepublic String getType() {return "MYSNOWFLAKE";}@Overridepublic boolean isDefault() {return true;}
}



使用spi机制加载我们上方定义的类

在这里插入图片描述



yml配置文件中使用我们自己定义的类

在这里插入图片描述



分片算法

开发者手册

ShardingAlgorithm

全限定类名org.apache.shardingsphere.sharding.spi.ShardingAlgorithm

分片算法,已知实现

配置标识自动分片算法详细说明类名
MODY基于取模的分片算法ModShardingAlgorithm
HASH_MODY基于哈希取模的分片算法HashModShardingAlgorithm
BOUNDARY_RANGEY基于分片边界的范围分片算法BoundaryBasedRangeShardingAlgorithm
VOLUME_RANGEY基于分片容量的范围分片算法VolumeBasedRangeShardingAlgorithm
AUTO_INTERVALY基于可变时间范围的分片算法AutoIntervalShardingAlgorithm
INTERVALN基于固定时间范围的分片算法IntervalShardingAlgorithm
CLASS_BASEDN基于自定义类的分片算法ClassBasedShardingAlgorithm
INLINEN基于行表达式的分片算法InlineShardingAlgorithm
COMPLEX_INLINEN基于行表达式的复合分片算法ComplexInlineShardingAlgorithm
HINT_INLINEN基于行表达式的 Hint 分片算法HintInlineShardingAlgorithm
COSID_MODN基于 CosId 的取模分片算法CosIdModShardingAlgorithm
COSID_INTERVALN基于 CosId 的固定时间范围的分片算法CosIdIntervalShardingAlgorithm
COSID_INTERVAL_SNOWFLAKEN基于 CosId 的雪花ID固定时间范围的分片算法CosIdSnowflakeIntervalShardingAlgorithm



实现

这里就拿CLASS_BASED自定义分片策略来举例。我们之前的配置项如下所示。

这里就有一个问题,props的值我怎么知道写什么,我又怎么知道我自定义的类需要实现什么接口?

在这里插入图片描述



我们现在进入到CLASS_BASED分片算法的实现类中ClassBasedShardingAlgorithm去看看它的源码

public final class ClassBasedShardingAlgorithm implements StandardShardingAlgorithm<Comparable<?>>, ComplexKeysShardingAlgorithm<Comparable<?>>, HintShardingAlgorithm<Comparable<?>> {// 定义两个常量,我们会发现这里就是props中我们进行配置的值private static final String STRATEGY_KEY = "strategy";private static final String ALGORITHM_CLASS_NAME_KEY = "algorithmClassName";@Getterprivate Properties props;private ClassBasedShardingAlgorithmStrategyType strategy;private String algorithmClassName;private StandardShardingAlgorithm standardShardingAlgorithm;private ComplexKeysShardingAlgorithm complexKeysShardingAlgorithm;private HintShardingAlgorithm hintShardingAlgorithm;// init()方法中会获取到props对象,props对象中保存了我们yml配置文件中的配置内容// 这里就会取出来,赋值给 strategy  和  algorithmClassName 成员属性@Overridepublic void init(final Properties props) {this.props = props;strategy = getStrategy(props);algorithmClassName = getAlgorithmClassName(props);initAlgorithmInstance(props);}private ClassBasedShardingAlgorithmStrategyType getStrategy(final Properties props) {String strategy = props.getProperty(STRATEGY_KEY);Preconditions.checkNotNull(strategy, "Properties `%s` can not be null when uses class based sharding strategy.", STRATEGY_KEY);return ClassBasedShardingAlgorithmStrategyType.valueOf(strategy.toUpperCase().trim());}private String getAlgorithmClassName(final Properties props) {String result = props.getProperty(ALGORITHM_CLASS_NAME_KEY);Preconditions.checkNotNull(result, "Properties `%s` can not be null when uses class based sharding strategy.", ALGORITHM_CLASS_NAME_KEY);return result;}// 这里就会判断 strategy 属性是哪一个  STANDARD、COMPLEX、HINT// 然后在进行具体的实例 StandardShardingAlgorithm、ComplexKeysShardingAlgorithm、HintShardingAlgorithmprivate void initAlgorithmInstance(final Properties props) {switch (strategy) {case STANDARD:standardShardingAlgorithm = ClassBasedShardingAlgorithmFactory.newInstance(algorithmClassName, StandardShardingAlgorithm.class, props);break;case COMPLEX:complexKeysShardingAlgorithm = ClassBasedShardingAlgorithmFactory.newInstance(algorithmClassName, ComplexKeysShardingAlgorithm.class, props);break;case HINT:hintShardingAlgorithm = ClassBasedShardingAlgorithmFactory.newInstance(algorithmClassName, HintShardingAlgorithm.class, props);break;default:break;}}// doSharding()方法,具体的分片算法逻辑@SuppressWarnings("unchecked")@Overridepublic String doSharding(final Collection<String> availableTargetNames, final PreciseShardingValue<Comparable<?>> shardingValue) {return standardShardingAlgorithm.doSharding(availableTargetNames, shardingValue);}@SuppressWarnings("unchecked")@Overridepublic Collection<String> doSharding(final Collection<String> availableTargetNames, final RangeShardingValue<Comparable<?>> shardingValue) {return standardShardingAlgorithm.doSharding(availableTargetNames, shardingValue);}@SuppressWarnings("unchecked")@Overridepublic Collection<String> doSharding(final Collection<String> availableTargetNames, final ComplexKeysShardingValue<Comparable<?>> shardingValue) {return complexKeysShardingAlgorithm.doSharding(availableTargetNames, shardingValue);}@SuppressWarnings("unchecked")@Overridepublic Collection<String> doSharding(final Collection<String> availableTargetNames, final HintShardingValue<Comparable<?>> shardingValue) {return hintShardingAlgorithm.doSharding(availableTargetNames, shardingValue);}// 返回trye为 CLASS_BASED  这里也就是和yml配置文件中的type对应上了@Overridepublic String getType() {return "CLASS_BASED";}
}



其他的分片算法也是类似的实现

在这里插入图片描述



扩展 自定义分片算法

自定义一个java类,实现ShardingAlgorithm接口,或者是它的子接口 StandardShardingAlgorithm、ComplexKeysShardingAlgorithm、HintShardingAlgorithm都行,重写其中的doSharding()方法,我们自己指定分片逻辑



重写getType()方法,返回一个字符串,能够让我们在yml配置文件中进行配置

@Override
public String getType() {return "MY_COMPLEX_ALGORITHM";
}



例如我现在自定义的分片类如下

package com.hs.sharding.algorithm;import com.google.common.base.Preconditions;
import com.google.common.collect.Range;
import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;import java.util.*;/*** 自定义分片策略 , 我们这里实现标准的分片算法接口StandardShardingAlgorithm* 我这里是分片逻辑就是按照个数取模,在分发到sys_user1   sys_user2数据表中*/
public class HsComplexAlgorithm implements StandardShardingAlgorithm<Long> {/*** 数据库个数*/private final String DB_COUNT = "db-count";/*** 数据表个数*/private final String TAB_COUNT = "tab-count";/*** 真实数据表前缀*/private final String PERTAB = "pertab";private Integer dbCount;private Integer tabCount;private String pertab;private Properties props;@Overridepublic void init(Properties props) {this.props = props;this.dbCount = getDbCount(props);this.tabCount = getTabCount(props);this.pertab = getPertab(props);// 校验条件Preconditions.checkState(null != pertab && !pertab.isEmpty(),"Inline hsComplex algorithm expression cannot be null or empty.");}/*** 精确查询分片执行接口(对应的sql是where ??=值)* @param collection 可用的分片名集合(分库就是库名,分表就是表名)* @param preciseShardingValue 分片键*/@Overridepublic String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) {Long uid = preciseShardingValue.getValue();String resultTableName = pertab + ((uid + 1) % (dbCount * tabCount) / tabCount + 1);if (collection.contains(resultTableName)){return resultTableName;}throw new UnsupportedOperationException("route: " + resultTableName + " is not supported, please check your config");}/*** 范围分片规则(对应的是where ??>='XXX' and ??<='XXX')* 范围查询分片算法(分片键涉及区间查询时会进入该方法进行分片计算)*/@Overridepublic Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Long> rangeShardingValue) {List<String> result = new ArrayList<>();Range<Long> valueRange = rangeShardingValue.getValueRange();Long upperEndpoint = valueRange.upperEndpoint();Long aLong = valueRange.lowerEndpoint();// TODO 进行相应的分片判断
//        return result;return collection;}private String getPertab(Properties props) {return props.getProperty(PERTAB);}private Integer getDbCount(Properties props) {String count = props.getProperty(DB_COUNT);return count == null || count.isEmpty() ? 0 : Integer.valueOf(count);}private Integer getTabCount(Properties props) {String count = props.getProperty(TAB_COUNT);return count == null || count.isEmpty() ? 0 : Integer.valueOf(count);}@Overridepublic Properties getProps() {return props;}@Overridepublic String getType() {return "HS";}
}



需要添加一个SPI的配置文件org.apache.shardingsphere.sharding.spi.ShardingAlgorithm,在该文件中指定我们上方创建的java类

在这里插入图片描述



yml配置文件中进行相应的更改

在这里插入图片描述



踩的坑

我先是自定义的类实现的是ComplexKeysShardingAlgorithm接口,但是我们yml配置类中还是一直按照standard的配置,导致我自定义的类中的doSharding()方法所以就一直没有调用到

在这里插入图片描述



之后我修改了complex就能调用了

在这里插入图片描述



在配置还是standard时,我通过debug,发现init()getType()方法都能够调用,证明SPI机制相关的文件没问题。

我就想会不会是单分片键、精确查询、范围查询相关问题导致的?

我修改了实现接口,改为了StandardShardingAlgorithm,然后就进入了其中单分片键的doSharding()方法。最后就一点一点的排查,再到了这上面的配置

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/394263.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

QT界面设计开发(Visual Studio 2019)—学习记录一

一、控件升级 简要介绍&#xff1a; 简单来说&#xff0c;控件提升就是将一个基础控件&#xff08;Base Widget&#xff09;转换为一个更特定、更复杂的自定义控件&#xff08;Custom Widget&#xff09;。这样做的目的是为了在设计界面时能够使用更多高级功能&#xff0c;而不…

环境搭建:全面详尽的 MongoDB Shell MongoDB Server介绍、安装、验证与配置指南(以 Windows 系统为主)

环境搭建&#xff1a;全面详尽的 MongoDB Shell & MongoDB Server介绍、安装、验证与配置指南&#xff08;以 Windows 系统为主&#xff09; MongoDB 是一个基于文档的 NoSQL 数据库&#xff0c;以其高性能、灵活性和可扩展性而受到广泛欢迎。本文将带您完成 MongoDB 的安装…

bpmn简单使用(制作流程图)

1、先下载依赖&#xff0c;下面是我下载的版本 "bpmn-io/properties-panel": "^3.23.0", "bpmn-js": "^17.9.1", "bpmn-js-properties-panel": "^5.6.1", "camunda-bpmn-moddle": "^7.0.1",…

CTFHUB-web-RCE-eval执行

开启题目 查看源码发现直接用蚁剑连接就可以&#xff0c;连接之后发现成功了

计算机网络408考研 2020

2020 湖科大教书匠的个人空间-湖科大教书匠个人主页-哔哩哔哩视频 计算机网络408考研 历年真题解析&#xff08;有字幕无背景音乐版&#xff09;_哔哩哔哩_bilibili 计算机网络408考研2020年真题解析_哔哩哔哩_bilibili 1 2 3 41 11 1

乡村振兴农村煤改气建设规划设计方案

1. 方案目标与背景 《乡村振兴农村煤改气建设规划设计方案》旨在响应国家乡村振兴战略&#xff0c;通过建设规划推动农村能源结构转型&#xff0c;减少燃煤造成的环境污染&#xff0c;促进农村可持续发展。 2. 农村能源消耗现状 根据2006至2007年的全国性调研&#xff0c;农…

从一个服务预热不生效问题谈微服务无损上线

作者&#xff1a;凡问、启淮 前言 本文基于阿里云技术服务团队和产研团队&#xff0c;在解决易易互联使用 MSE&#xff08;微服务引擎&#xff09;产品无损上线功能所遇到问题的过程总结而成。本文将从问题和解决方法谈起&#xff0c;再介绍相关原理&#xff0c;后进一步拓展…

4.11.seq2seq 序列到序列学习

序列到序列学习(seq2seq) ​ 使用两个循环神经网络的编码器和解码器&#xff0c;应用于序列到薛烈类的学习任务。 ​ ​ 在图中&#xff0c;特定的"<eos>"表示序列结束词元。一旦输出序列生成此词元&#xff0c;模型就会停止预测。在循环神经网络解码器的初…

JS+CSS案例:可适应上下布局和左右布局的菜单(含二级菜单)

今天,我给大家分享一个原创的CSS菜单,整个菜单全由CSS写成,仅在切换布局时使用JS。合不合意,先看看效果图。 本例图片 接下来,我来详细给大家分享它的制作方法。 文件夹结构 因为涉及到了样式表切换,所以,你需要借鉴一下我的文件夹结构。 CSS文件夹: reset.css 用于…

维吉尼亚密码加解密实现(python)

维吉尼亚密码 原理 维吉尼亚密码&#xff08;Vigenere&#xff09;是使用一系列凯撒密码组成密码字母表的加密算法&#xff0c;属于多表密码的一种简单形式。 下面给出一个例子 明文&#xff1a;come greatwall 密钥&#xff1a;crypto首先&#xff0c;对密钥进行填充使其长…

【算法】普里姆算法解决修路问题

应用场景——修路问题 1.某地有 7 个村庄&#xff08;A&#xff0c;B&#xff0c;C&#xff0c;D&#xff0c;E&#xff0c;F&#xff0c;G&#xff09;&#xff0c;现在需要修路把 7 个村庄连通 2.各个村庄的距离用边线表示&#xff08;权&#xff09;&#xff0c;比如 A - …

ORM工具之SQLAlchemy

SQLAlchemy是Python编程语言下的一款开源软件。提供了SQL工具包及对象关系映射&#xff08;ORM&#xff09;工具&#xff0c;使用MIT许可证发行。 SQLAlchemy“采用简单的Python语言&#xff0c;为高效和高性能的数据库访问设计&#xff0c;实现了完整的企业级持久模型”。SQL…

从 Pandas 到 Polars 四十四:Polars 和 数据可视化库Seaborn

在我对Matplotlib感到沮丧并发表帖子时&#xff0c;我的朋友让我试试Seaborn库。近年来我一直在使用Altair&#xff0c;因此并没有过多考虑Seaborn。然而&#xff0c;Seaborn的新界面给我留下了深刻印象&#xff0c;并且我很高兴地发现&#xff0c;Seaborn将直接接受Polars的Da…

【web安全】权限漏洞之未授权访问

一.Jenkins未授权访问漏洞 步骤一&#xff1a;使用以下fofa语法进行搜索 port"8080" && app"JENKINS" && title"Dashboard [Jenkins]" 步骤二&#xff1a;进入执行页面http://xxx.xxx.xxx.xxx:xxxx/manage/script/index.php 执…

Linux下自动监控进程运行状态

目录 背景应用举例1、使用crontab脚本监控服务2、使用shell脚本监控服务2.1 编写自定义监控脚本2.2 运行脚本 背景 假设有一个服务需要长期运行&#xff0c;但可能会由于某种原因导致服务意外停止&#xff0c;不能及时发现&#xff0c;某天来到公司后发现出问题了才意识到服务…

(Qt) QThread 信号槽所在线程

文章目录 &#x1f481;&#x1f3fb;前言&#x1f481;&#x1f3fb;Code&#x1f481;&#x1f3fb;‍♂️Code&#x1f481;&#x1f3fb;‍♂️环境 &#x1f481;&#x1f3fb;当前线程信号&#x1f481;&#x1f3fb;‍♂️默认效果&#x1f481;&#x1f3fb;‍♂️Qt::…

最新CSS3伪类和伪元素详解

第4章 伪类和伪元素 4.1结构伪类 E:first-child{},第一个元素 样式&#xff1a; p:first-child {color: red; } <div><p>Lorem ipsum</p><p>Dolor sit amet.</p> </div> 4.1.1nth-*伪类 以计数为基础的&#xff0c;默认情况下&…

探索下一代互联网协议:IPv6的前景与优势

探索下一代互联网协议&#xff1a;IPv6的前景与优势 文章目录 探索下一代互联网协议&#xff1a;IPv6的前景与优势**IPv6 的特点****IPv6的基本首部****IPv6的地址****总结** 互联网的核心协议&#xff1a;从IPv4到IPv6 互联网的核心协议IP&#xff08;Internet Protocol&#…

Docker Deskpot出现Docker Engine Stopped的解决历程

前提&#xff1a;我的操作系统是Win11家庭版, Docker Descktop下载的是最新版&#xff08;此时是4.30.0&#xff09; 出现了如图所示的问题“Docker Engine Stopped”,个人认为解决问题的关键是第四点&#xff0c;读者可以直接看第四点&#xff0c;如果只看第四点就成功解决&am…

python开发上位机 - PyCharm环境搭建、安装PyQt5及工具

目录 简介&#xff1a; 一、安装PyCharm 1、下载 PyCharm 2、PyCharm安装 1&#xff09;配置安装目录 2&#xff09;安装选项 3、问题及解决方法 二、安装PyQt5 1、打开 Pycharm&#xff0c;新建 Project 2、安装 pyqt5 3、安装很慢怎么办&#xff1f; 4、安装 pyq…