后端存储实战课——海量数据篇

news2024/11/20 9:27:57

海量数据导致存储系统慢

拆,将一大坨数据拆分成 N 个小坨,学名「分片」。

归档历史数据

将大量的不常用的历史数据移到另外一张历史表中,大概流程:
在这里插入图片描述

批量删除大量数据

不能一次性直接删除,需要分批删除(并在每次删除之间停一会儿):

delete from orderswhere timestamp < SUBDATE(CURDATE(),INTERVAL 3 month)order by id limit 1000;

优化思路:先查出来符合条件的 id,然后根据 id 去删除:

select max(id) from orders
where timestamp < SUBDATE(CURDATE(),INTERVAL 3 month);


delete from orders
where id <= ?
order by id limit 1000;

删除数据后表空间没有被释放原因:虽然逻辑上每个表是一颗 B+ 树,但是物理上,每条记录都是存放在磁盘文件中的,这些记录通过一些位置指针来组织成一颗 B+ 树。当 MySQL 删除一条记录的时候,只能是找到记录所在的文件中位置,然后把文件的这块区域标记为空闲,然后再修改 B+ 树中相关的一些指针,完成删除。其实那条被删除的记录还是躺在那个文件的那个位置,所以并不会释放磁盘空间。

解决方案:可以执行一次 OPTIMIZE TABLE 释放存储空间。对于 InnoDB 来说,执行 OPTIMIZE TABLE 实际上就是把这个表重建一遍,执行过程中会一直锁表,也就是说这个时候下单都会被卡住,这个是需要注意的。前提条件是 MySQL 的配置必须是每个表独立一个表空间(innodb_file_per_table = ON),如果所有表都是放在一起的,执行 OPTIMIZE TABLE 也不会释放磁盘空间。

分库分表

原则

能不拆就不拆,能少拆就不多拆。因为数据拆得越散,开发和维护就越复杂。

解决的问题

一是解决数据量大了之后查询慢的问题,此处说的查询是事务中的查询和更新,只读的查询可以通过缓存或者主从分离来解决。解决查询慢,还是需要尽可能减少查询的数据总量,使用分表来解决。

二是为了应对高并发的问题,一个实例撑不住,我们可以将请求分散到不同的实例解决,使用分库来解决。

Sharding Key 选择

最重要的参考因素:业务如何访问数据

以我的订单页面为例:如果将订单的 ID 作为 Sharding Key 拆分,查询条件是用户 ID,没法知道查询的订单在哪个分片上,只能查找所有分片得出查询结果,性能差还麻烦。如果以用户 ID 为 Sharding Key,查询条件是订单 ID,就不知道订单在哪个分片上了。

上述的问题可以通过在生成订单 ID 的时候,把用户的 ID 位数作为订单 ID 的一部分,按照订单 ID 查询的时候,可以根据订单 ID 中的用户 ID 找到对应分片。但是真实业务中往往不只有用两个 ID 作为查询条件。

一般做法:将订单数据同步到其它存储系统中,在其它存储系统中解决。

构建一个以店铺 ID 作为 Sharding Key 的只读订单库,专门供商家使用;或者将数据导入到 HDFS,使用大数据技术生成订单的报表。

分片算法选择

前提:希望并发请求和数据能均衡到分不到每一个分片上

按照时间范围分片:容易产生热点问题,但是查询友好(加上时间范围的条件),适合数据量大并发访问量不大的 ToB 系统。

哈希分片算法:有最简单的取模算法(直接取余),或者一致性哈希,分得均匀的前提是作为 Sharding Key 的后几位数字是均匀分布的。

查表法:将分配结果记录到一个表里,执行查询时先查该表,然后去找对应存储的分片。整体上比较灵活,可以手动将数据分均匀,可以随时改变分片,也可以通过缓存加速查询。

Redis 集群

Redis 官方在 3.0 版本开始提供了 Redis Cluser 这种集群方式,类似 MySQL 分库分表,这种方式是通过分片的方式,来保存更多的数据。

分片的大概规则是通过 CRC16 算法算出来一个值,然后将此值除以 16384,余数就是 Key 所在的槽。

在集群初始化的时候,Redis 会自动平均分配 16384 个槽(也可以用命令调整),也会将「槽与 Redis 实例的映射关系」保存在每个 Redis 实例中。

客户端请求一个 key 的时候,被请求的 Redis 会先通过上面的公式,计算出这个 Key 在哪个槽中,然后再查询槽和节点的映射关系,找到数据所在的真正节点,如果这个节点正好是自己,那就直接执行命令返回结果。如果数据不在当前这个节点上,那就给客户端返回一个重定向的命令,告诉客户端,应该去连哪个节点上请求这个 Key 的数据。然后客户端会再连接正确的节点来访问。

如果需要增加节点水平扩容增大集群的存储容量,需要将老节点中搬运一些槽到新节点中,这一步可以通过手动指定,也可以用官方的 redis-trib.rb 脚本实现。

Redis Cluster 高可用是通过增加从节点,做主从复制实现的。

主从原理:读的时候主从都可以读,写的时候只写主库然后同步到从库

  1. 启动一台 slave 的时候,他会发送一个 psync 命令给 master ,如果是这个 slave 第一次连接到 master,他会触发一个全量复制。master 就会启动一个线程,生成 RDB 快照,还会把新的写请求都缓存在内存中,RDB 文件生成后,master 会将这个 RDB 发送给 slave 的,slave 拿到之后做的第一件事情就是写进本地的磁盘,然后加载进内存;同步期间的对主库写操作会记录到 replication buffer,待第二步完成后发送给从库。
  2. 一旦主从库完成了全量复制,它们之间就会一直维护一个网络连接,主库会通过这个连接将后续陆续收到的命令操作再同步给从库,这个过程也称为基于长连接的命令传播,可以避免频繁建立连接的开销。
  3. 网络断连后,通过增量复制同步。

注意:Redis Cluster 适合构建中小规模(几个到几十个节点) Redis 集群,不适合做超大规模集群,原因是因为其采用了 流言协议 来传播集群配置的变化,虽然部署和维护简单,但是传播会比较慢。

如果想构建超大规模集群可以使用 twemproxy 以及 codis,主要原理是在客户端和 Redis 节点之间增加了一层代理服务,可以起到转发客户端请求、监控节点状态、维护集群云数据的作用。缺点是增长了数据访问链路,带来一定的性能损失。还有一种方案是先查询元数据,查到要访问的分配和节点,然后直接找对应的 Redis 查询(元数据一般不咋变,变了的话,需要重新维护)。

附:搭建步骤

MySQL 与 Redis 同步

一是可以通过 MQ 订阅相关数据变更业务主题,然后专门提供一个更新缓存的服务,此服务消费消息,来更新缓存。

二是使用 binlog 实时更新 Redis 缓存。

后者不用考虑发送消息失败等数据一致性的问题,但是解析 binlog 相对麻烦,可以通过 Canal 来实现接收 binlog 来更新 Redis 缓存。
在这里插入图片描述

对象存储

对象存储是原生的分布式存储系统(相对于 MySQL 单机存储系统),有很好的大文件读写功能,还可以通过水平扩展实现几乎无限的容量,可以兼顾服务高可用、数据高可靠的特性。

大概原理

KV 存储,对象存储内部有存储节点,用于保存文件,这些节点就是数据节点的集群。

为了管理数据节点及其文件,需要一个存储系统保存节点信息、文件信息以及对应的映射关系,这个存储系统中的数据称为元数据。

为了对外提供访问服务,需要网关集群,对外接收外部请求,对内访问元数据和数据节点。

对象存储保存大文件的时候,会将大文件拆分成多个大小相等的块儿。

原因:

  1. 提升读写性能,块儿分配到不同的数据节点吗,可以并行读写
  2. 大小相等,便于维护

为了方便管理,还会有一个将块儿们聚合一下放到一个容器里(理解的是放的块儿的地址?)
在这里插入图片描述

跨系统实时同步数据

如果用本地消息表的方式(分布式事务),将一份数据实时同步给另外的数据库,会对业务有侵入性。

如果下游数据库多的话,只用 Canal 的话,可能写不过来,还有就是不同的下游可能会有不同的数据转换和过滤工作要做。可以通过加入 MQ 的方式来搞:Canal 从 MySQL 收到 binlog 并解析成结构化数据后,直接写到 MQ 的主题中,下游业务系统订阅此主题,消费解析到的 binlog 数据,然后入库。附:Canal 接入 MQ 文档。

在这里插入图片描述
对于瓶颈 3 来说,也不能随意增加并发来提升处理能力,原因是有的业务是有顺序要求的(如果更新 A B 两个订单,这两个订单的谁先更新谁后更新没关系,但是如果更新同一个订单,就得涉及到严格的顺序了),要做的是保证每个订单的更新操作日志不乱序就可以了。这种一致性称为因果一致性,有因果关系的数据之间必须要严格地保证顺序,没有因果关系的数据之间的顺序是无所谓的。

扩展:
MySQL 主从同步 Binlog,是一个单线程的同步过程。为什么是单线程?原因很简单,在从库执行 Binlog 的时候,必须按顺序执行,才能保证数据和主库是一样的。为了确保数据一致性,Binlog 的顺序很重要,是绝对不能乱序的。 严格来说,对于每一个 MySQL 实例,整个处理链条都必须是单线程串行执行,MQ 的主题也必须设置为只有 1 个分区(队列),这样才能保证数据同步过程中的 Binlog 是严格有序的,写到目标数据库的数据才能是正确的。

并行进行数据同步的具体做法:首先根据下游同步程序的消费能力,计算出需要多少并发;然后设置 MQ 中主题的分区(队列)数量和并发数一致。因为 MQ 是可以保证同一分区内,消息是不会乱序的,所以我们需要把具有因果关系的 binlog 都放到相同的分区中去,就可以保证同步数据的因果一致性。对应到订单库就是,相同订单号的 binlog 必须发到同一个分区上。这是不是和之前讲过的数据库分片有点儿像呢?那分片算法就可以拿过来复用了,比如我们可以用最简单的哈希算法,binlog 中订单号除以 MQ 分区总数,余数就是这条 binlog 消息发往的分区号。

不停机更换数据库

  1. 上线同步程序,从旧库中复制数据到新库中,并实时保持同步;
  2. 上线双写订单服务(改变 DAO 层,支持双写「只写旧、只写新、同步双写」,支持双读「读旧、读新」,可以通过预留接口实现切换),只读写旧库;
  3. 开启双写,同时停止同步程序(先写旧库、再写新库,以写旧库结果为准「如果旧库成功新库失败,返回成功,记录写新库失败原因;如果旧库失败新库成功,返回失败,不让新库影响现有业务可用性和数据准确性);
  4. 开启对比和补偿程序,确保新旧数据库数据完全一样(一是停止同步程序和开启双写,这两个过程很难做到无缝衔接,二是双写的策略也不保证新旧库强一致,这时候我们需要上线一个对比和补偿的程序,这个程序对比旧库最近的数据变更,然后检查新库中的数据是否一致,如果不一致,还要进行补偿);
  5. 逐步切量读请求到新库上;
  6. 下线对比补偿程序,关闭双写,读写都切换到新库上;
  7. 下线旧库和订单服务的双写功能。

如果双写切换为单写新库出现问题,可采用类似 3 一样的程序:从双写以旧库为准,过渡到双写以新库为准。然后把比对和补偿程序反过来,用新库的数据补偿旧库的数据。这样就可以做到,一旦出问题,再切回到旧库上了。

海量数据存储

数据量大的几类数据:点击流数据(在 App、小程序和 Web 页面上的埋点数据,记录了用户的行为:比如说打开的页面、使用了哪些按钮、留存时间等)、监控数据、日志数据。

之前存储这些数据倾向于「先计算后存储」,顾名思义,就是先做一些过滤、聚合等初步计算,然后再存储,降低存储系统压力和成本。

随着存储也来也便宜,数据价值被不断挖掘,现在更多倾向于「先存储后计算」,直接保存海量的数据,再对数据进行实时或者批量的计算,主要有以下优点:

  • 不需要二次分发就可以同时给多个流和批计算任务提供数据;
  • 如果计算任务出错,可以随时回滚重新计算;
  • 如果对数据有新的分析需求,上线后直接就可以用历史数据计算出结果,而不用去等新数据。

对存储系统的要求:容量足够大、能够水平扩容、要跟上数据生产的写入以及给下游提供低延迟的读数据服务。常用的解决方案有 Kafka 以及HDFS。

Kafka

Kafka 官方给自己的定位也是“分布式流数据平台”,不只是一个 MQ。Kafka 提供“无限”的消息堆积能力,具有超高的吞吐量,可以满足我们保存原始数据的大部分要求。写入点击流数据的时候,每个原始数据采集服务作为一个生产者,把数据发给 Kafka 就可以了。下游的计算任务,可以作为消费者订阅消息,也可以按照时间或者位点来读取数据。并且,Kafka 作为事实标准,和大部分大数据生态圈的开源软件都有非常好的兼容性和集成度,像 Flink、Spark 等大多计算平台都提供了直接接入 Kafka 的组件。
在这里插入图片描述
存在的问题:容量并不是真正无限的,写入数据的时候会将数据均匀分散到分片上,但是分片对应的节点容量终究是有限的,分片总会写满,虽然支持扩容分片数量,但是 不能像其他分布式存储那样重新分配数据,所以扩容分片不能解决已有分片写满的问题,又不支持按照时间维度分片,所以受制于单节点的存储容量,Kafka 实际存储的容量并不是无限的。

HDFS

使用 HDFS 来存储。使用 HDFS 存储数据也很简单,就是把原始数据写成一个一个文本文件,保存到 HDFS 中。

对于保存海量的原始数据这个特定的场景来说,HDFS 的吞吐量是远不如 Kafka 的。按照平均到每个节点上计算,Kafka 的吞吐能力很容易达到每秒钟大几百兆,而 HDFS 只能达到百兆左右。这就意味着,要达到相同的吞吐能力,使用 HDFS 就要比使用 Kafka,多用几倍的服务器数量。

但 HDFS 也有它的优势,第一个优势就是,它能提供真正无限的存储容量,如果存储空间不够了,水平扩容就可以解决。另外一个优势是,HDFS 能提供比 Kafka 更强的数据查询能力。Kafka 只能按照时间或者位点来提取数据,而 HDFS 配合 Hive 直接就可以支持用 SQL 对数据进行查询,虽然说查询的性能比较差,但查询能力要比 Kafka 强大太多了。

扩展

分布式流数据存储:pravega、Pulsar 的存储引擎 Apache BookKeeper

时序数据库:influxdb、OpenTSDB

海量数据查询

将数据存起来之后,还得通过流计算(Flink、Storm 这类的实时计算)或者批计算(Map-Reduce 或者 Spark 这类的非实时计算)将原始数据进行过滤、汇聚等加工计算,将计算结果存到别的存储系统中使用以供业务系统提供查询支持。

如果你的系统的数据量在 GB 量级以下,MySQL 仍然是可以考虑的,因为它的查询能力足以应付大部分分析系统的业务需求。并且可以和在线业务系统合用一个数据库,不用做 ETL(数据抽取),省事儿并且实时性好。这里还是要提醒你,最好给分析系统配置单独的 MySQL 实例,避免影响线上业务。

如果数据量级已经超过 MySQL 极限,可以选择一些列式数据库,比如:HBase、Cassandra、ClickHouse,这些产品对海量数据,都有非常好的查询性能,在正确使用的前提下,10GB 量级的数据查询基本上可以做到秒级返回。高性能的代价是功能上的缩水,这些数据库对数据的组织方式都有一些限制,查询方式上也没有 MySQL 那么灵活。大多都需要你非常了解这些产品的脾气秉性,按照预定的姿势使用,才能达到预期的性能。

另外一个值得考虑的选择是 Elasticsearch(ES),ES 本来是一个为了搜索而生的存储产品,但是也支持结构化数据的存储和查询。由于它的数据都存储在内存中,并且也支持类似于 Map-Reduce 方式的分布式并行查询,所以对海量结构化数据的查询性能也非常好。

最重要的是,ES 对数据组织方式和查询方式的限制,没有其他列式数据库那么死板。也就是说,ES 的查询能力和灵活性是要强于上述这些列式数据库的。在这个级别的几个选手中,强烈建议你优先考虑 ES。但是 ES 有一个缺点,就是你需要给它准备大内存的服务器,硬件成本有点儿高。

New SQL

MySQL经常遇到的高可用、分片问题,NewSQL是如何解决的?

Redis 和很多 KV 存储系统,性能上各种吊打 MySQL,而且因为存储结构简单,所以比较容易组成分布式集群,并且能够做到水平扩展、高可靠、高可用。因为这些 KV 存储不支持 SQL,为了以示区分,被统称为 No SQL。

No SQL 本来希望能凭借高性能和集群的优势,替代掉 Old SQL。但用户是用脚投票的,这么多年实践证明,你牺牲了 SQL 这种强大的查询能力和 ACID 事务支持,用户根本不买账,直到今天,Old SQL 还是生产系统中最主流的数据库。

这个时候,大家都开始明白了,无论你其他方面做的比 Old SQL 好再多,SQL 和 ACID 是刚需,这个命你革不掉的。你不支持 SQL,就不会有多少人用。所以你看,近几年很多之前不支持 SQL 的数据库,都开始支持 SQL 了,甚至于像 Spark、Flink 这样的流计算平台,也都开始支持 SQL。当然,虽然说支持 SQL,但这里面各个产品的支持程度是参差不齐的,多多少少都有一些缩水。对于 ACID 的支持,基本上等同于就没有。

这个时候,New SQL 它来了!简单地说,New SQL 就是兼顾了 Old SQL 和 No SQL 的优点:

  • 完整地支持 SQL 和 ACID,提供和 Old SQL 隔离级别相当的事务能力;
  • 高性能、高可靠、高可用,支持水平扩容。

像 Google 的 Cloud Spanner、国产的 OceanBase 以及开源的 CockroachDB 都属于 New SQL 数据库。

扩展:

架构:
在这里插入图片描述
架构仍然是大部分数据库都采用的二层架构:执行器和存储引擎。它的 SQL 层就是执行器,下面的分布式 KV 存储集群就是它的存储引擎。

采用 Raft 一致性协议来实现每个分片的高可靠、高可用和强一致。

设计文档:Design

隔离级别:CockroachDB 提供了两种隔离级别,是:Snapshot Isolation (SI) 和 Serializable Snapshot Isolation (SSI)
在这里插入图片描述

RocksDB

RocksDB:不丢数据的高性能KV存储
理解的是既是个数据库,也是个存储引擎。

读写原理:,使用了 LSM-Tree。全称是:The Log-Structured Merge-Tree,是一种非常复杂的复合数据结构,它包含了 WAL(Write Ahead Log)、跳表(SkipList)和一个分层的有序表(SSTable,Sorted String Table)
在这里插入图片描述
我们先来看数据是如何写入的。当 LSM-Tree 收到一个写请求,比如说:PUT foo bar,把 Key foo 的值设置为 bar。首先,这条操作命令会被写入到磁盘的 WAL 日志中(图中右侧的 Log),这是一个顺序写磁盘的操作,性能很好。这个日志的唯一作用就是用于故障恢复,一旦系统宕机,可以从日志中把内存中还没有来得及写入磁盘的数据恢复出来。

写完日志之后,数据可靠性的问题就解决了。然后数据会被写入到内存中的 MemTable 中,这个 MemTable 就是一个按照 Key 组织的跳表(SkipList),跳表和平衡树有着类似的查找性能,但实现起来更简单一些。写 MemTable 是个内存操作,速度也非常快。数据写入到 MemTable 之后,就可以返回写入成功了。这里面有一点需要注意的是,LSM-Tree 在处理写入的过程中,直接就往 MemTable 里写,并不去查找这个 Key 是不是已经存在了。

这个内存中 MemTable 不能无限地往里写,一是内存的容量毕竟有限,另外,MemTable 太大了读写性能都会下降。所以,MemTable 有一个固定的上限大小,一般是 32M。MemTable 写满之后,就被转换成 Immutable MemTable,然后再创建一个空的 MemTable 继续写。这个 Immutable MemTable,也就是只读的 MemTable,它和 MemTable 的数据结构完全一样,唯一的区别就是不允许再写入了。
Immutable MemTable 也不能在内存中无限地占地方,会有一个后台线程,不停地把 Immutable MemTable 复制到磁盘文件中,然后释放内存空间。每个 Immutable MemTable 对应一个磁盘文件,MemTable 的数据结构跳表本身就是一个有序表,写入的文件也是一个按照 Key 排序的结构,这些文件就是 SSTable。把 MemTable 写入 SSTable 这个写操作,因为它是把整块内存写入到整个文件中,这同样是一个顺序写操作。

到这里,虽然数据已经保存到磁盘上了,但还没结束,因为这些 SSTable 文件,虽然每个文件中的 Key 是有序的,但是文件之间是完全无序的,还是没法查找。这里 SSTable 采用了一个很巧妙的分层合并机制来解决乱序的问题。

SSTable 被分为很多层,越往上层,文件越少,越往底层,文件越多。每一层的容量都有一个固定的上限,一般来说,下一层的容量是上一层的 10 倍。当某一层写满了,就会触发后台线程往下一层合并,数据合并到下一层之后,本层的 SSTable 文件就可以删除掉了。合并的过程也是排序的过程,除了 Level 0(第 0 层,也就是 MemTable 直接 dump 出来的磁盘文件所在的那一层。)以外,每一层内的文件都是有序的,文件内的 KV 也是有序的,这样就比较便于查找了。

然后我们再来说 LSM-Tree 如何查找数据。查找的过程也是分层查找,先去内存中的 MemTable 和 Immutable MemTable 中找,然后再按照顺序依次在磁盘的每一层 SSTable 文件中去找,只要找到了就直接返回。这样的查找方式其实是很低效的,有可能需要多次查找内存和多个文件才能找到一个 Key,但实际的效果也没那么差,因为这样一个分层的结构,它会天然形成一个非常有利于查找的情况:越是被经常读写的热数据,它在这个分层结构中就越靠上,对这样的 Key 查找就越快。

比如说,最经常读写的 Key 很大概率会在内存中,这样不用读写磁盘就完成了查找。即使内存中查不到,真正能穿透很多层 SStable 一直查到最底层的请求还是很少的。另外,在工程上还会对查找做很多的优化,比如说,在内存中缓存 SSTable 文件的 Key,用布隆过滤器避免无谓的查找等来加速查找过程。这样综合优化下来,可以获得相对还不错的查找性能。

关联阅读

后端存储实战课——设计篇

后端存储实战课——高速增长篇

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

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

相关文章

SpringBoot【创建与使用】

SpringBoot【创建与使用】&#x1f34e;一.SpringBoot是什么&#x1f352;1.1 SpringBoot的优点&#x1f34e;二.SpringBoot的创建&#x1f352;2.1 使⽤ Idea 中央源创建&#x1f349;2.1.1 下载插件&#x1f349;2.1.2 创建项目&#x1f349;2.1.3 项目的加载&#x1f349;2.…

倪健中:全球元宇宙与中国文化精神 | 钱学森诞辰111周年系列活动开幕仪式

编者按&#xff1a; 倪健中会长出席纪念“中国元宇宙之父”钱学森诞辰111周年线上开幕式并发表了云致辞。 在致辞中&#xff0c;倪会长高度崇敬和赞扬钱学森对中国元宇宙事业做出的伟大贡献。我们因钱老的伟大思想&#xff0c;在探索元宇宙与中国传统文化哲学的融合进程中&…

【Kafka从成神到升仙系列 五】面试官问我 Kafka 生产者的网络架构,我直接开始从源码背起.......

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱敲代码的小黄&#xff0c;独角兽企业的Java开发工程师&#xff0c;CSDN博客专家&#xff0c;Java领域新星创作者&#x1f4d5;系列专栏&#xff1a;Java设计模式、数据结构和算法、Kafka从入门到成神、Kafka从成神到…

Spring 6 源码编译和高效阅读源码技巧分享

一. 前言 Spring Boot 3 RELEASE版本于 2022年11月24日 正式发布&#xff0c;相信已经有不少同学开始准备新版本的学习了&#xff0c;不过目前还不建议在实际项目中做升级&#xff0c;毕竟还有很多框架和中间件没出适配版本。此次Spring Boot里程碑的升级也要求了最低JDK 17 和…

风靡互联网关键词 Web3.0 | 区块链 | 元宇宙……

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; Web web是互联网的总称&#xff0c;全称为World Wide Web&#xff0c;缩写WWW &#xff0c;即全球广域网&#xff0c;也称为万维网&#xff0c;它是一种基于超文本和H…

nacos--基础--1.4--理论--原理

nacos–基础–1.4–理论–原理 1、基本架构 2、Nacos 原理 2.1、信息的同步主要的几种方式 push (服务端主动push)pull (客户端的轮询)&#xff0c; 超时时间比较短long pull (超时时间比较长) 2.2、配置中心原理 nacos 配置中心就是采用&#xff1a;客户端 long pull 的方式…

新体制SAR——BiDi SAR

1. 概述 TerraSAR的相控阵天线具备方位向和距离向二维电扫能力&#xff0c;在这一能力的基础上&#xff0c;TerraSAR可以工作在条带模式、ScanSAR模式、滑聚模式和TOPS模式&#xff08;TOPS是实验模式&#xff0c;不是主模式&#xff09;。载荷的PRF可以设计在3-6.5kHz&#xf…

【笔试强训】Day 7

&#x1f308;欢迎来到笔试强训专栏 (꒪ꇴ꒪(꒪ꇴ꒪ )&#x1f423;,我是Scort目前状态&#xff1a;大三非科班啃C中&#x1f30d;博客主页&#xff1a;张小姐的猫~江湖背景快上车&#x1f698;&#xff0c;握好方向盘跟我有一起打天下嘞&#xff01;送给自己的一句鸡汤&#x…

安科瑞AcrelEMS-SW智慧水务能效管理平台解决方案

系统概述 安科瑞电气具备从终端感知、边缘计算到能效管理平台的产品生态体系&#xff0c;AcrelEMS-SW智慧水务能效管理平台通过在污水厂源、网、荷、储、充的各个关键节点安装保护、监测、分析、治理装置&#xff0c;用于监测污水厂能耗总量和能耗强度&#xff0c;重点监测主要…

2022年史上最全Java面试题:数据结构+算法+JVM+线程+finalize+GC

基本概念 操作系统中 heap 和 stack 的区别 什么是基于注解的切面实现 什么是 对象/关系 映射集成模块 什么是 Java 的反射机制 什么是 ACID BS与CS的联系与区别 Cookie 和 Session的区别 fail-fast 与 fail-safe 机制有什么区别 get 和 post请求的区别 Interface 与 …

服务端高并发分布式架构演进之路

1. 概述 本文以淘宝作为例子&#xff0c;介绍从一百个到千万级并发情况下服务端的架构的演进过程。同时列举出每个演进阶段会遇到的相关技术&#xff0c;让大家对架构的演进有一个整体的认知。文章最后汇总了一些架构设计的原则。 特别说明&#xff1a;本文以淘宝为例仅仅是为…

如何入门学python,这是很值得借鉴的学习方法

前言 众所周知&#xff0c;python的应用领域十分广泛&#xff0c;无论是对于专业的程序员还是从事其他工作的人&#xff0c;python这门编程语言都非常值得学习。 但对于零基础的人来说&#xff0c;该如何入门python编程呢&#xff1f; 虽然现在网上有关python编程的教程很多…

PyFlink系列之一:PyFlink安装和PyFlink使用的详细技术

PyFlink系列之一&#xff1a;PyFlink安装和PyFlink使用的详细技术一、下载PyFlink二、创建TableEnvironment三、TableEnvironment API1.Table/SQL 操作2.执行/解释作业3.创建/删除用户自定义函数4.依赖管理5.配置四、Catalog APIs五、Statebackend&#xff0c;Checkpoint 以及重…

小程序图片加载失败binderror方法处理

场景&#xff1a;我们在小程序项目中的一个图片列表&#xff0c;当某些图片加载失败后&#xff0c;直接显示空白&#xff0c;这样用户体验不好&#xff0c;为了解决当图片加载失败&#xff0c;我们给一个默认图片代替&#xff0c;参考官方给的图片加载失败的处理方法&#xff1…

C51单片机开发程序报错 main.c (11) : error C267 : ‘Func‘ : requires ANSI-style prototype

问题 C51单片机开发程序报错 main.c (11) : error C267 : Func : requires ANSI-style prototype详细问题 问题一 问题二 问题三 可能原因一 函数定义声明处&#xff08;.h文件中&#xff09;与主函数中函数&#xff08;函数名/参数类型/返回值类型&#xff09;不一致 解决…

【Vue2+Element ui通用后台】项目搭建和vue-router使用

文章目录介绍创建项目并引入Element-ui按需引入全局引入vue-router安装嵌套路由介绍 通过这个系列文章&#xff0c;我们将学到&#xff1a; 1.项目搭建使用element实现首页布局 2.顶部导航菜单及与左侧导航联动的面包屑实现 3.封装—个ECharts组件 4.封装一个Form表单组件和Ta…

木字楠后台管理系统开发(4):SpringSecurity引入并编写登陆接口

&#x1f3b6; 文章简介&#xff1a;木字楠后台管理系统开发(4)&#xff1a;SpringSecurity引入并编写登陆接口 &#x1f4a1; 创作目的&#xff1a;为了带大家完整的体验木字楠后台管理系统模版的开发流程 ☀️ 今日天气&#xff1a;冬天来啦&#xff01; &#x1f4dd; 每日一…

在ubuntu上部署gitlab详细步骤

一、Ubuntu安装gitlab步骤&#xff1a; 安装依赖 通过快捷键ctrlaltT打开命令行窗口&#xff0c;然后运行下面两行命令 sudo apt update sudo apt-get upgrade sudo apt-get install curl openssh-server ca-certificates postfix 如果这一步遇到下面提示界面&#xff0c…

BUUCTF Web2

[HCTF 2018]admin flask session的伪造 改密码的页面源码有提示&#xff0c;得到秘钥ckj123 自己的session .eJw9kEGLwjAUhP_KkrOHJm09CB5cbKULeaHwanm5iKu1adK4UBVpxP--XRc8zGmGj5l5sN1paC6GLa7DrZmxXXdkiwf7-GYLptCl2uoOcHWXmDu1kYnGLIFNdQdsBYmtkbb3YI89YDXKUHKNTkCg8S9PliJ…

Kotlin 开发Android app(二十二):Retrofit和简单的mvp框架

到这一节&#xff0c;基本上把大部分kotlin和android的开发都已经介绍完成了&#xff0c;通过了前面和这一章的框架结构&#xff0c;基本上能解决开发中的很多问题&#xff0c;并且能够知道android的主要的技术&#xff0c;并进行独立开发了。对于传统的开发的话&#xff0c;还…