探究Kafka原理-6.CAP理论实践

news2024/11/28 6:48:52
  • 👏作者简介:大家好,我是爱吃芝士的土豆倪,24届校招生Java选手,很高兴认识大家
  • 📕系列专栏:Spring源码、JUC源码、Kafka原理
  • 🔥如果感觉博主的文章还不错的话,请👍三连支持👍一下博主哦
  • 🍂博主正在努力完成2023计划中:源码溯源,一探究竟
  • 📝联系方式:nhs19990716,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬👀

文章目录

  • 消息的精准消费
  • kafka 系统的 CAP 保证
    • 分布式系统的 CAP 理论
      • 分区容错性:
      • 可用性:
      • 一致性:
    • 分区副本机制
    • 分区副本的数据一致性困难
      • 问题 1:分区副本间动态不一致
      • 问题 2:消费者所见不一致
      • 问题 3:分区副本间最终不一致
    • 一致性问题解决方案(HW)
      • 解决方案的核心思想
      • 解决“消费者所见不一致” (消费者只允许看到 HW 以下的 message)
      • 解决“分区副本数据最终不一致” (follower 数据按HW截断)
    • HW 方案的天生缺陷
    • HW 会产生数据丢失和副本最终不一致问题
    • Leader-Epoch 机制的引入
      • leader epoch 如何解决 HW 的备份缺陷
        • 解决数据丢失:
        • 解决数据最终不一致问题:
    • LEO/HW/LSO 等相关术语速查
    • 不清洁选举[了解]

消息的精准消费

在前面学到的手动提交位移的时机选择的时候

  • 数据处理完成之前先提交偏移量

可能会发生漏处理的现象(数据丢失)

反过来说,这种方式实现了: at most once 的数据处理(传递)语义

  • 数据处理完成之后再提交偏移量

可能会发生重复处理的现象(数据重复)

反过来说,这种方式实现了: at least once 的数据处理(传递)语义

当然,数据处理(传递)的理想语义是: exactly once(精确一次)

Kafka 也能做到 exactly once(基于 kafka 的事务机制)

实现中,可以记录为 消息存储在一张表,然后偏移量存储在一张表,但是还是有可能出现问题,除非绑定为原子操作。

在这里插入图片描述

相当于 偏移量的更新 和 业务数据的落地绑定成一个事务

begin transaction

insert into tb1 values();
insert into t_offset values();

commit

还有一种办法就是 利用幂等性,重复就重复,但是插入数据库中的机会就只有一次,那么就能达到最终一致的效果。

所以解决数据重复的问题,有两种解决办法:

1.利用事务

2.利用幂等性

而解决数据丢失的问题,主要有三种解决办法:

  1. 启用Kafka的事务机制:Kafka提供了事务机制,可以将消息的处理和偏移量的提交放在同一个事务中进行,确保消息的处理和偏移量的提交是原子性的。通过事务机制,可以避免在数据处理完成之前就提交偏移量而导致数据丢失的问题。
  2. 手动控制偏移量的提交:可以在应用程序中手动控制偏移量的提交时机。例如,可以在消息处理完成并且已经被确认成功后再提交偏移量。这样可以确保消息得到正确处理后再进行偏移量的提交,避免数据丢失的问题。
  3. 使用At-Least-Once语义:在消费者的配置中设置enable.auto.commitfalse,然后手动提交偏移量。在消息处理过程中,确保消息处理的幂等性,即多次处理同一条消息的结果是一致的。这样即使存在重复消息的情况,也能保证数据最终被处理一次。
public class Consumer实现ExactlyOnce手段1{
    public static void main(String[] args){
        // 定义 kakfa 服务的地址,不需要将所有 broker 指定上
		props.put("bootstrap.servers", "doitedu01:9092");
        // 制定 consumer group
		props.put("group.id", "g1");
        // key 的反序列化类
		props.put("key.deserializer",
"org.apache.kafka.common.serialization.StringDeserializer");
        // value 的反序列化类
		props.put("value.deserializer",
"org.apache.kafka.common.serialization.StringDeserializer");
        // 是否自动提交 offset 
		props.put("enable.auto.commit", "false");
        // 如果没有消费偏移量记录,则自动重设为起始 offset:latest, earliest, none
		props.put("auto.offset.reset","earliest");
        
        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
        
        
        
        // 创建一个jdbc连接
        Connection conn = DriverManager.getConnection("jdbc:mysql://test:3306/abc","root","123456");
        
        conn.setAutoCommit(false);
        
        PreparedStatement pstData = conn.prepareStatement("insert into stu_info values (?,?,?,?)");
        
        PreparedStatement pstOffset = conn.prepareStatement("insert into t_offsets values (?,?) on DUPLICATE KEY UPDATE offset = ?");
        
        // 需要把消费起始位置,初始化成上一次运行所记录的消费位移中去。
        // 而且还需要考虑一个问题,消费者再均衡时会发生什么(这是一个非常严重的问题!!!)启动的时候也是要发生再均衡的。
        // 这才是真正意义上的生产意义的代码
        consumer.subscribe(Arrays.asList("user-info"));
        
        
        boolean run = true;
        while(run){
            ConsumerRecords<String, String> records = consumer.poll(100);
			for (ConsumerRecord<String, String> record : records)
                try{
                    String data = record.value();
            		// 解析原始数据
            		String[] fields = data.split(",");
            		// 插入mysql
            		......
               	     
               	 	// 执行业务数据插入语句
               	 	pstData.execute();
                	// 更新mysql中记录的偏移量
            
            		pstOffset.setString(1,record.topic+":"+record.partition());
            		pstOffset.setLong(2,record.offset()+1);
            		pstOffset.setLong(3,record.offset()+1);
            
            	
            
            		pstOffset.execute();
            		// 数据没提交,mysql自动回滚
            		conn.commit();
                }catch(Exception e){
                    conn.rollback();
                }
                
			}
        }
    }
}

起始位置的初始化、接续,要考虑两个环节:

1.程序启动时初始化

2.程序正常运行过程中发生了消费再均衡的过程,也需要进行起始位置的重新初始化

PreparedStatement pstQueryOffset = conn.prepareStatement("select offset from t_offsets where topic_partition = ?");


consumer.subscribe(Arrays.asList("user-info"),new ConsumerRebalanceListener(){
    // 被剥夺分区消费权后会调用下面的方法
    public void onPartitionsRevoked(Collection<TopicPartition> partitions){
        // 如果某些场景下,不能用事务去收拾残局的话,可以在这个方法里面收拾
    }
    // 被分配了新的分区消费权后调用的方法
    public void onPartitionsAssigned(Collection<TopicPartition> partitions){
        try{
            // 去查询mysql 中的 t_offsets表,得到自己拥有消费权的分区的消费位移记录
            for(TopicPartition topicPartition : partitions){
                pstQueryOffset.setString(1,topicPartition.topic() + ":" + topicPartition.partition());
                ResultSet resultSet = pstQueryOffset.executeQuery();
                resultSet.next();
                long offset = resultSet.getLong("offset");
                // 将消费初始位置初始化为 数据库中查询到的偏移量
                consumer.seek(topicPartition,offset);
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }
});

我们的这种方法比较彻底,根本就不用kafka去提交偏移量,而是将偏移量存储在mysql中。

consumer的消费位移提交方式:

1.全自动 auto.offset.commit = true; 定时提交到 consumer_offsets中。

2.半自动 auto.offset.commit = false; 然后手动触发提交,然后手动触发提交到 consumer.commitSync() -> 提交到consumer_offset中去。

提交到consumer_offset中的好处是初始化的时候会自动去找上一个offset

3.全手动 auto.offset.commit = false; 写自己的代码,去把消费者位移保存到你自己的地方 mysql等,将来初始化也需要自己自己去从自定义存储中查询到消费者的位移。

kafka 系统的 CAP 保证

分布式系统的 CAP 理论

CAP 理论作为分布式系统的基础理论,它描述的是一个分布式系统在以下三个特性中:

  • 一致性(Consistency)
  • 可用性(Availability)
  • 分区容错性(Partition tolerance)

最多满足其中的两个特性。也就是下图所描述的。分布式系统要么满足 CA,要么 CP,要么 AP。无法同时满足 CAP。

分区容错性:

指的分布式系统中的某个节点或者网络分区出现了故障的时候,整个系统仍然能对外提供满足一致性和可用性的服务。也就是说部分故障不影响整体使用。

事实上我们在设计分布式系统是都会考虑到 bug,硬件,网络等各种原因造成的故障,所以即使部分节点或者网络出现故障,我们要求整个系统还是要继续使用的

(不继续使用,相当于只有一个分区,那么也就没有后续的一致性和可用性了)

可用性:

一直可以正常的做读写操作。简单而言就是客户端一直可以正常访问并得到系统的正常响应。用户角度来看就是不会出现系统操作失败或者访问超时等问题。

一致性:

在分布式系统完成某写操作后任何读操作,都应该获取到该写操作写入的那个最新的值。相当于要求分布式系统中的各节点时时刻刻保持数据的一致性。


kafka做的处理是,为了保证数据的一致性,数据的读写永远找分区里的leader,但是就会造成系统的可用性降低了,而kafka为了提升可用性,如果分区内其它副本挂了,其它的还可能成为leader,但是一致性可能降低,(因为有可能做不到完全同步)总之这三个特性很难完全的去满足。

Kafka 作为一个商业级消息中间件,数据可靠性和可用性是优先考虑的重点,兼顾数据一致性;

分区副本机制

kafka 从 0.8.0版本开始引入了分区版本,也就是说每个分区可以认为的配置几个副本(创建主题的时候指定replication-factor,也可以在broker级别进行配置 default.replication.factor);

在众多的分区副本里面有一个副本是Leader,其余的副本是follower,所有的读写操作都是经过Leader进行的,同时follower会定期地去leader复制数据(通过心跳机制去请求同步数据)。当Leader挂了的时候,其中一个follower会重新成为新的Leader。通过分区服务,引入了数据冗余,同时也提供了kafka的数据可靠性。

Kafka的分区多副本架构是Kafka可靠性保证的核心,把消息写入多个副本可以使Kafka在发生崩溃时仍能保证消息的持久性。

分区副本的数据一致性困难

kafka 让分区多副本同步的基本手段是: follower 副本定期向 leader 请求数据同步!
既然是定期同步,则 leader 和 follower 之间必然存在各种数据不一致的情景!

问题 1:分区副本间动态不一致

在这里插入图片描述

问题 2:消费者所见不一致

如果此时 leader 宕机,follower1 或 follower2 被选为新的 leader,则 leader 换届前后,消费者所能读取到的数据发生了不一致;

在这里插入图片描述

问题 3:分区副本间最终不一致

在这里插入图片描述

一致性问题解决方案(HW)

动态过程中的副本数据不一致,是很难解决的;

kafka 先尝试着解决上述“消费者所见不一致”及“副本间数据最终不一致”的问题;

解决方案的核心思想

在动态不一致的过程中,维护一条步进式的“临时一致线”(既所谓的 High Watermark)

高水位线 HW = ISR 副本中最小 LEO(最大结束偏移量 + 1)

底层逻辑就是:offset < HW 的message,是各副本间一致的且安全的!

在这里插入图片描述

(如上图所示:offset < hw : 3 的message,是所有副本都已经备份好的数据)

高水位涉及到了数据的一致性。

在这里插入图片描述

leader中会记录 remoteLEO

在这里插入图片描述

在这里插入图片描述

等到在请求的时候,携带的LEO就会变了

在这里插入图片描述

此时leader这边的hw将会变成 6 ,再来请求的时候发给的就是6了

等到follow再次同步的时候,才能知道hw是多少

解决“消费者所见不一致” (消费者只允许看到 HW 以下的 message)

在这里插入图片描述

解决“分区副本数据最终不一致” (follower 数据按HW截断)

在这里插入图片描述

high watermark

代表数据在多副本备份的进度

hw就代表offset < hw的数据已经在所有isr副本间全部备份完毕。

所以,offset < hw的数据,让消费者可见,是相对安全的

HW 方案的天生缺陷

如前所述,看似 HW 解决了“分区数据最终不一致”的问题,以及“消费者所见不一致”的问题,但其实,这里面存在一个巨大的隐患,导致:

“分区数据最终不一致”的问题依然存在

producer 设置 acks=all 后,依然有可能丢失数据的问题

产生如上结果的根源是:HW 高水位线的更新,与数据同步的进度,存在迟滞!

在这里插入图片描述

Step 1:leader 和 follower 副本处于初始化值,follower 副本发送 fetch 请求,由于 leader 副本没有数据,因此不会进行同步操作;

Step 2:生产者发送了消息 m1 到分区 leader 副本,写入该条消息后 leader 更新 LEO = 1;

Step 3:follower 发送 fetch 请求,携带当前最新的 offset = 0,leader 处理 fetch 请求时,更新 remote LEO = 0,对比 LEO 值最小为 0,所以 HW = 0,leader 副本响应消息数据及 leader HW = 0 给follower,follower 写入消息后,更新 LEO 值,同时对比 leader HW 值,取最小的作为新的 HW 值,此时 follower HW = 0,这也意味着,follower HW 是不会超过 leader HW 值的。

Step 4:follower 发送第二轮 fetch 请求,携带当前最新的 offset = 1,leader 处理 fetch 请求时,更新 remote LEO = 1,对比 LEO 值最小为 1,所以 HW = 1,此时 leader 没有新的消息数据,所以直接返回 leader HW = 1 给 follower,follower 对比当前最新的 LEO 值 与 leader HW 值,取最小的作为新的 HW 值,此时 follower HW = 1。

从以上步骤可看出,leader 中保存的 remote LEO 值的更新(也即 HW 的更新)总是需要额外一轮fetch RPC 请求才能完成,这意味着在 leader 切换过程中,会存在数据丢失以及数据不一致的问题!

HW 会产生数据丢失和副本最终不一致问题

数据丢失的问题(即使 produce 设置 acks=all,依然会发生)

在这里插入图片描述

注意回顾:leader 中的 HW 值是在 follower 下一轮 fetch RPC 请求中完成更新的

如上图所示:

  • 状态起始: B 为 leader,A 为 follower; 最新消息 m2 已同步,但 B 的 HW 比 A 的HW 大
  • A 在此时崩溃(即 follower 没能通过下一轮请求来更新 HW 值)
  • A 重启时,会自动将 LEO 值调整到之前的 HW 值,即会进行日志截断
  • B 重启后,会从 向 A 发送 fetch 请求,收到 fetch 响应后,拿到 HW 值,并更新本地 HW 值,这时 B 会做日志截断,因此,offsets = 1 的消息被永久地删除了。

副本间数据最终不一致的问题(即使 produce 设置 acks=all,依然会发生)

在这里插入图片描述

如上图所示:

  • 状态起始: A 为 leader,B 为 follower; 最新消息 m2 已同步,但 B 的 HW 比 A 的 HW 小
  • A 在此时崩溃(即 follower 没能通过下一轮请求来更新 HW 值)
  • B 先重启,会自动将 LEO 值调整到之前的 HW 值,即会进行日志截断,并在此刻接收了新的消息 m3,HW 随之上升为 2
  • 然后,A 重启上线,会从 向 B 发送 fetch 请求,收到 fetch 响应后,拿到 HW 值,并更新本地 HW 值,发现不需要截断,从而已经产生了“副本间数据最终不一致”!

只要新一届 leader 在老 leader 重启上线前,接收了新的数据,就可能发生上图中的场景,根源也在于HW 的更新落后于数据同步进度

Leader-Epoch 机制的引入

为了解决 HW 更新时机是异步延迟的,而 HW 又是决定日志是否备份成功的标志,从而造成数据丢失和数据不一致的现象,Kafka 引入了 leader epoch 机制;

在每个副本日志目录下都创建一个 leader-epoch-checkpoint 文件,用于保存 leader 的 epoch 信息

leader-epoch 的含义

如下,leader epoch 长这样:

在这里插入图片描述

它的格式为 (epoch offset),epoch 指的是 leader 版本,它是一个单调递增的一个正整数值,每次 leader变更,epoch 版本都会 +1,offset 是每一代 leader 写入的第一条消息的位移值,比如:

(0,0)
(1,300)

以上第 2 个版本是从位移 300 开始写入消息,意味着第一个版本写入了 0-299 的消息。

这里面记录信息的本质试:从哪个offset开始的数据,是从那届leader写入的。

leader epoch 具体的工作机制:

  • 当副本成为 leader 时:

这时,如果此时生产者有新消息发送过来,会首先更新 leader epoch 以及 LEO ,并添加到leader-epoch-checkpoint 文件中

  • 当副本变成 follower 时

发送 LeaderEpochRequest 请求给 leader 副本,该请求包括了 follower 中最新的 epoch 版本;leader 返回给 follower 的响应中包含了一个 LastOffset,如果 follower last epoch = leader last epoch(纪元相同),则 LastOffset = leader LEO,否则取 大于 follower last epoch 中最小的 leader epoch 的 start offset值;

举个例子:假设 follower last epoch = 1,此时 leader 有 (1, 20) (2, 80) (3, 120),则 LastOffset = 80;

follower 拿到 LastOffset 之后,会对比当前 LEO 值是否大于 LastOffset,如果当前 LEO 大于LastOffset,则从 LastOffset 截断日志;follower 开始发送 fetch 请求给 leader 保持消息同步。

leader epoch 如何解决 HW 的备份缺陷

解决数据丢失:

在这里插入图片描述

如上图所示:

A 重启之后,发送 LeaderEpochRequest 请求给 B,由于 B 还没追加消息,此时 epoch = request epoch = 0,因此返回 LastOffset = leader LEO = 2 给 A

A 拿到 LastOffset 之后,发现等于当前 LEO 值,故不用进行日志截断。就在这时 B 宕机了,A 成为 leader,在 B 启动回来后,会重复 A 的动作,同样不需要进行日志截断,数据没有丢失。

解决数据最终不一致问题:

在这里插入图片描述

如上图所示:

  • A 和 B 同时宕机后,B 先重启回来成为分区 leader,这时候生产者发送了一条消息过来,leader epoch 更新到 1
  • 此时 A 启动回来后,发送 LeaderEpochReques(t follower epoch = 0)给 B,B 判断 follower epoch不等于最新的 epoch,于是找到大于 follower epoch 最小的 epoch = 1,即 LastOffset = epoch start offset = 1
  • A 拿到 LastOffset 后,判断小于当前 LEO 值,于是从 LastOffset 位置进行日志截断,接着开始发送 fetch 请求给 B 开始同步消息,避免了消息不一致/离散的问题。

HW的时候,计算producer把acks = all,依然会丢数据,因为它是依赖HW来进行数据截断的。

而HW的更新是相对于数据同步进度落后一轮请求的。

而现在,acks = all依赖于 leader epoch的话,不会再有数据丢失发生了,也不会再有消息不一致情况了。

LEO/HW/LSO 等相关术语速查

LEO:(last end offset)就是该副本中消息的最大偏移量的值+1

HW:(high watermark)各副本中 LEO 的最小值。这个值规定了消费者仅能消费 HW 之前的数据

LEO 与 HW 与数据一致性密切相关;

在这里插入图片描述

如图,各副本中最小的 LEO 是 3,所以 HW 是 3,所以,消费者此刻最多能读Msg2;

不清洁选举[了解]

不清洁选举,是指允许“非 ISR 副本”可以被选举为 leader;非 ISR 副本被选举为 leader,将极大增加数据丢失及数据不一致的可能性!由参数 unclean.leader.election.enable=false(默认) 控制;

初始状态: follower2 严重落后于 leader,并且不属于 ISR 副本

在这里插入图片描述

此刻,所有 ISR 副本宕机

在这里插入图片描述

Follower2 成为新的 leader,并接收数据

在这里插入图片描述

之前宕机副本重启,按照最新 leader 的最新 leo 进行截断,产生数据丢失及不一致

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

华为智能手表独立导航,一呼即应轻松畅行

PetalMaps 手表独立导航&#xff0c;一声令下唤醒导航&#xff0c;打造了智慧的语音交互唤醒体验功能。导航时&#xff0c;语音播报、变道震动提醒功能&#xff0c;让您尽情体验腕上导航乐趣&#xff0c;同时又能安全抵达目的地。

如何在外远程访问本地NAS威联通QNAP?

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;数据结构、Cpolar杂谈 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. 威联通安装cpolar内网穿透二. 内网穿透2.1 创建隧道2.2 测试公网远程访问 三.…

51单片机的智能窗帘系统【含proteus仿真+程序+报告+原理图】

1、主要功能 该系统由AT89C51单片机LCD1602显示模块DS18B20温度模块DS1302时间模块光敏传感器步进电机蓝牙等模块构成。适用于智能窗帘、智能门窗家具等相似项目。 可实现基本功能: 版本一&#xff1a; 1、LCD1602实时显示北京时间、环境温度、光照强度、手动/自动控制等信息…

vatee万腾的数字探险:Vatee科技创新的未知征程

在科技风潮的巅峰&#xff0c;Vatee万腾如一艘科技探险的航船&#xff0c;勇敢地驶向未知的数字化征程。 Vatee万腾在数字探险的过程中展现出征服未知领域的坚定决心。他们不满足于现状&#xff0c;而是积极地寻找和探索那些尚未被揭示的数字化领域。这种决心使得Vatee能够在科…

眼见非实-MISC-bugku-解题步骤

——CTF解题专栏—— 题目信息&#xff1a; 题目&#xff1a;眼见非实 作者&#xff1a;harry 提示&#xff1a;无 解题附件&#xff1a; 解题思路&#xff1a; 眼见非实&#xff1f;&#xff1f;&#xff1f;难道要用手摸一下&#xff1f;&#xff08;开玩笑.jpg&#xff…

第一篇:快速入门

简介 本篇文章主要目的教你如何快速的理解、掌握cocos shader的相关知识&#xff0c;并附加实践案例。 shader 我们可以理解为是一种在图形渲染过程中控制像素颜色的过程&#xff0c;通常用来创建各种视觉效果。如光照、阴影、扭曲等。 Material&#xff08;材质&#xff0…

safari浏览器,直接安装ipa文件

蒲公英二维码方法 个人开发者账号发布证书AD-hoc 描述文件蒲公英上传链接通过苹果safari 浏览器下载IPA包 浏览器下载方法 前置条件 1.下载 ipa 包的设备的 uuid 已加入 苹果测试设备列表如何添加到测试列表 2.web 服务, 文件服务. 3.需要AD-hoc 描述文件 添加链接描述 1.创…

华大基因在合规管理、高质量发展方面将迈上新的台阶

今年6月&#xff0c;华大基因顺利通过了国际领先标准、测试及认证机构BSI的严格审核&#xff0c;获得GB/T 35770-2022 / ISO 37301:2021合规管理体系认证证书&#xff0c;成为行业内率先获此国际认证的企业。 ISO 37301合规管理体系认证是国际通用的合规管理体系认证标准&…

【Java Spring】SpringBoot 日志系统

文章目录 一、Spring Boot 日志系统1.1 Spring Boot 日志框架1.2 自定义日志打印1.3 日志级别设置1.4 日志持久化1.5 lombok 简化日志输出 一、Spring Boot 日志系统 1.1 Spring Boot 日志框架 SLF4J 和 logback都是spring boot内置的日志框架&#xff0c;开发者只负责调用对…

opencv-医学图像预处理

医学图像预处理通常需要针对特定任务和数据集的特点进行定制。以下是一些常见的医学图像预处理步骤&#xff0c;可以使用OpenCV以及其他相关库来实现&#xff1a; 导入相关的库 import cv2 import matplotlib.pyplot as plt1. 读取图像 image cv2.imread(r"C:\Users\m…

YOLOv8-Seg改进:自适应改变核大小卷积AKConv,效果优于标准卷积核和DSConv |2023.11月最新成果

🚀🚀🚀本文改进: AKConv 中,通过新的坐标生成算法定义任意大小的卷积核的初始位置。 为了适应目标的变化,引入了偏移量来调整每个位置的样本形状。 此外,我们通过使用具有相同大小和不同初始采样形状的 AKConv 来探索神经网络的效果。 AKConv 通过不规则卷积运算完成…

8.二维数组——将一个二维数组行和列的元素互换,存到另一个二维数组中。

文章目录 前言一、题目描述 二、题目分析 三、解题 程序运行代码 前言 本系列为二维数组编程题&#xff0c;点滴成长&#xff0c;一起逆袭。 一、题目描述 将一个二维数组行和列的元素互换&#xff0c;存到另一个二维数组中。 二、题目分析 三、解题 程序运行代码 #incl…

AI 绘画 | Stable Diffusion 提示词扩展插件

前言 提示词对于Stable Diffusion AI绘画来说非常重要, 由于Stable Diffusion 支持英文提示词,对于英文不好的朋友,每次都要切换翻译网站去翻译,很不方便,下面介绍两款Stable Diffusion 提示词扩展插件,让你写提示词更轻松。 sd-webui-prompt-all-in-one 提示词多合一插…

区块链介绍

区块链提供了比特币的公共账本&#xff0c;这是一个有序的、带有时间戳的交易记录。这个系统用于防止重复消费和修改之前的交易记录。 Introduction 比特币网络中的每个完全节点都独立存储只包含该节点验证的块的区块链。当多个节点在他们的区块链中都有相同的块时&#xff0…

PMIC : 一颗芯片解决N多问题

1、什么是PMIC Power Management Integrated Circuit&#xff08;PMIC&#xff09;中文是电源管理集成电路&#xff0c;主要特点是高集成度&#xff0c;将传统的多路输出电源封装在一颗芯片内&#xff0c;使得多电源应用场景高效率更高&#xff0c;体积更小。 PMIC 是当今电子…

如何选择到适合自己的IP代理服务商?IPIDEA为您分享

随着互联网的发展&#xff0c;越来越多的企业开始依赖网络进行各种业务&#xff0c;对于代理IP这个工具来说&#xff0c;应该都不陌生&#xff0c;尤其是大数据、跨境行业的企业&#xff0c;为了让出海业务更顺利&#xff0c;也为了保护企业的数据安全和隐私&#xff0c;许多企…

c/c++ 字符 - ‘0‘ 或者 + ‘0‘ 的含义

总的就是说&#xff0c;int0char;char-0int &#xff0c;但是我们在做题时&#xff0c;这两个式子对数字才有意义&#xff0c;比如 char x50;int y5-0. 而我们平常对字符操作&#xff0c;比如大写字符转小写&#xff08;char cA->a&#xff09;,只需要cc-Aa&#xff0c;等…

6.一维数组——用冒泡法将10个整数由大到小排序

文章目录 前言一、题目描述 二、题目分析 三、解题 程序运行代码 前言 本系列为一维数组编程题&#xff0c;点滴成长&#xff0c;一起逆袭。 一、题目描述 用冒泡法将10个整数由大到小排序 二、题目分析 三、解题 程序运行代码 #include<stdio.h> int main() {int …

MySQL实现高可用方案-MHA安装及配置

MySQL高可用性解决方案Master High Availability (MHA) 是一种在 MySQL 故障转移环境中实现快速故障转移和数据保护的开源软件。MHA 能在 MySQL 主节点发生故障时&#xff0c;自动将备节点提升为主节点&#xff0c;并且不会中断正在进行的 SQL 操作。 需求&#xff1a;主从配置…

P22 C++数组

目录 前言 01 什么是数组 02 如何定义数组 2.1 那么如何设置和访问这些整数呢&#xff1f; 2.2 小心内存越界 2.3 for循环遍历数组 03 在堆上创建数组 前言 本期我们讨论 C 中的数组。 在我们开始讨论数组之前&#xff0c;要先理解指针是什么&#xff0c;因为指针基本上…