大数据-106 Spark Graph X 计算学习 案例:1图的基本计算、2连通图算法、3寻找相同的用户

news2024/12/24 1:00:04

点一下关注吧!!!非常感谢!!持续更新!!!

目前已经更新到了:

  • Hadoop(已更完)
  • HDFS(已更完)
  • MapReduce(已更完)
  • Hive(已更完)
  • Flume(已更完)
  • Sqoop(已更完)
  • Zookeeper(已更完)
  • HBase(已更完)
  • Redis (已更完)
  • Kafka(已更完)
  • Spark(正在更新!)

章节内容

上节完成了如下的内容:

  • Spark Graph X
  • 基本概述
  • 架构基础
  • 概念详解
  • 核心数据结构
    在这里插入图片描述

编写 Spark GraphX 程序注意的事情

数据分区与负载均衡

由于 GraphX 运行在分布式环境中,数据分区策略直接影响到计算性能。合理分区可以减少网络传输和计算开销,提高图计算的效率。要注意图数据的分布情况,避免数据倾斜。

处理大规模数据时的内存管理

GraphX 会对顶点和边的数据进行分区和缓存,但在处理大规模图数据时,内存管理尤为重要。需要注意内存使用情况,合理配置 Spark 的内存参数,避免内存溢出或垃圾回收频繁的问题。

迭代计算的收敛条件

许多图算法(如 PageRank)是基于迭代计算的,因此要合理设置收敛条件(例如迭代次数或结果变化阈值)。过多的迭代会浪费计算资源,过少的迭代可能导致结果不准确。

图的变换和属性操作

在对图进行操作时,特别是更新顶点和边的属性时,要确保变换操作不会导致数据不一致或图结构的破坏。使用 mapVertices、mapEdges 等操作时,要谨慎处理每个顶点和边的属性。

错误处理与调试

在编写分布式程序时,错误处理和调试尤为重要。GraphX 的操作涉及复杂的图结构,调试时应充分利用 Spark 的日志和错误信息,使用小规模数据集进行初步验证,逐步扩展到大规模数据。

数据存储与序列化

GraphX 在处理大规模图数据时,可能需要将数据保存到外部存储中(如 HDFS)。要注意选择合适的数据格式和序列化方式,以保证数据读写的高效性和可靠性。

扩展性与性能优化

在开发 GraphX 应用时,考虑到未来可能的扩展需求,程序设计应具有一定的扩展性。同时,针对性能的优化也是关键,要通过测试和调整参数来找到最佳的执行配置。

编写 Spark GraphX 程序

以下是编写 Spark GraphX 程序的主要步骤:

初始化 SparkContext

创建 SparkConf 和 SparkContext,这是 Spark 应用程序的入口。

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.graphx._

object GraphXExample {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("GraphX Example").setMaster("local[*]")
    val sc = new SparkContext(conf)
  }
}

构建顶点和边 RDD

顶点和边是构建图的基本元素。我们可以通过 RDD 来定义这些元素

// 顶点RDD (VertexId, 属性)
val vertices: RDD[(VertexId, String)] = sc.parallelize(Array(
  (1L, "Alice"), 
  (2L, "Bob"), 
  (3L, "Charlie"), 
  (4L, "David")
))

// 边RDD (源顶点ID, 目标顶点ID, 属性)
val edges: RDD[Edge[Int]] = sc.parallelize(Array(
  Edge(1L, 2L, 1), 
  Edge(2L, 3L, 1), 
  Edge(3L, 4L, 1), 
  Edge(4L, 1L, 1)
))

构建图 (Graph)

使用顶点和边的 RDD 来构建图。

val graph = Graph(vertices, edges)

进行图操作或算法计算

你可以对图进行各种操作或使用图算法库进行计算。下面的示例是计算 PageRank。

val ranks = graph.pageRank(0.01).vertices

收集和处理结果

通过 collect 或 saveAsTextFile 等方法获取和处理计算结果。

ranks.collect().foreach { case (id, rank) => 
  println(s"Vertex $id has rank: $rank") 
}

关闭 SparkContext

在程序结束时,关闭 SparkContext 以释放资源。

sc.stop()

导入依赖

<dependency>
  <groupId>org.apache.spark</groupId>
  <artifactId>spark-graphx_2.12</artifactId>
  <version>${spark.version}</version>
</dependency>

案例一:图的基本计算

在这里插入图片描述

编写代码

package icu.wzk

import org.apache.spark.graphx.{Edge, Graph, VertexId}
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

object GraphExample1 {

  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
      .setAppName("GraphExample1")
      .setMaster("local[*]")
    val sc = new SparkContext(conf)
    sc.setLogLevel("WARN")

    // 初始化数据
    // 定义定点(Long,info)
    val vertexArray: Array[(VertexId, (String, Int))] = Array(
      (1L, ("Alice", 28)),
      (2L, ("Bob", 27)),
      (3L, ("Charlie", 65)),
      (4L, ("David", 42)),
      (5L, ("Ed", 55)),
      (6L, ("Fran", 50))
    )

    // 定义边(Long,Long,attr)
    val edgeArray: Array[Edge[Int]] = Array(
      Edge(2L, 1L, 7),
      Edge(2L, 4L, 2),
      Edge(3L, 2L, 4),
      Edge(3L, 6L, 3),
      Edge(4L, 1L, 1),
      Edge(5L, 2L, 2),
      Edge(5L, 3L, 8),
      Edge(5L, 6L, 3),
    )

    // 构造vertexRDD和edgeRDD
    val vertexRDD: RDD[(Long, (String, Int))] = sc.makeRDD(vertexArray)
    val edgeRDD: RDD[Edge[Int]] = sc.makeRDD(edgeArray)

    // 构造图Graph[VD,ED]
    val graph: Graph[(String, Int), Int] = Graph(vertexRDD, edgeRDD)

    // 属性操作实例
    // 找出图中年龄大于30的顶点
    graph.vertices
      .filter {
        case (_, (_, age)) => age > 30
      }
      .foreach(println)

    // 找出图中属性大于5的边
    graph.edges
      .filter {
        edge => edge.attr > 5
      }
      .foreach(println)

    // 列出边属性 > 5 的triplets
    graph.triplets
      .filter(t => t.attr > 5)
      .foreach(println)

    // degrees操作
    // 找出图中最大的出度、入度、度数
    println("==========outDegrees=============")
    graph.outDegrees.foreach(println)
    val outDegrees: (VertexId, Int) = graph.outDegrees
      .reduce {
        (x, y) => if (x._2 > y._2) x else y
      }
    println(s"Out degree: ${outDegrees}")

    println("==========inDegrees=============")
    graph.inDegrees.foreach(println)
    val inDegrees: (VertexId, Int) = graph.inDegrees
      .reduce {
        (x, y) => if (x._2 > y._2) x else y
      }
    println(s"In degree: ${inDegrees}")

    // 转换操作
    // 顶点的转换操作 所有人年龄+10岁
    graph.mapVertices {
      case (id, (name, age)) => (id, (name, age + 10))
    }
      .vertices
      .foreach(println)

    // 边的转换操作 边的属性 * 2
    graph.mapEdges(e => e.attr * 2)
      .edges
      .foreach(println)

    // 结构操作
    // 顶点年龄 > 30的子图
    val subGraph: Graph[(String, Int), Int] = graph.subgraph(vpred = (id, vd) => vd._2 >= 30)
    println("==========SubGraph=============")
    subGraph.vertices.foreach(println)
    subGraph.edges.foreach(println)

    // 连接操作
    println("============连接操作==============")
    // 创建一个新图 顶点VD的数据类型 User,并从Graph做类型转换
    val initialUserGraph: Graph[User, Int] = graph.mapVertices {
      case (_, (name, age)) => User(name, age, 0, 0)
    }
    // initialUserGraph 与 inDegree outDegree 进行 JOIN 修改 inDeg outDeg
    var userGraph: Graph[User, Int] = initialUserGraph
      .outerJoinVertices(initialUserGraph.inDegrees) {
        case (id, u, inDegOut) => User(u.name, u.age, inDegOut.getOrElse(0), u.outDeg)
      }
      .outerJoinVertices(initialUserGraph.outDegrees) {
        case (id, u, outDegOut) => User(u.name, u.age, u.inDeg, outDegOut.getOrElse(0))
      }

    userGraph.vertices.foreach(println)

    // 找到 出度=入度 的人员
    userGraph.vertices
      .filter {
      case (id, u) => u.inDeg == u.outDeg
    }
      .foreach(println)

    // 聚合操作
    // 找到5到各顶点的最短距离
    // 定义源点
    val sourceId: VertexId = 5L
    val initialGraph: Graph[Double, Int] = graph
      .mapVertices((id, _) => if (id == sourceId) 0.0 else Double.PositiveInfinity)
    val sssp: Graph[Double, Int] = initialGraph.pregel(Double.PositiveInfinity)(
      // 两个消息来的时候,取它们当中路径的最小值
      (id, dist, newDist) => math.min(dist, newDist),
      // Send Message 函数
      // 比较 triplet.srcAttr + triplet.attr 和 triplet.dstAttr
      // 如果小于,则发送消息到目的顶点
      triplet => {
        // 计算权重
        if (triplet.srcAttr + triplet.attr < triplet.dstAttr) {
          Iterator((triplet.dstId, triplet.srcAttr + triplet.attr))
        } else {
          Iterator.empty
        }
      },
      // mergeMsg
      (a, b) => Math.min(a, b)
    )

    println("找到5到各个顶点的最短距离")
    println(sssp.vertices.collect.mkString("\n"))

    sc.stop()

  }
}

case class User(name: String, age: Int, inDeg: Int, outDeg: Int)

运行结果

(5,(Ed,55))
(6,(Fran,50))
(3,(Charlie,65))
(4,(David,42))
Edge(2,1,7)
Edge(5,3,8)
((5,(Ed,55)),(3,(Charlie,65)),8)
((2,(Bob,27)),(1,(Alice,28)),7)
==========outDegrees=============
(5,3)
(3,2)
(2,2)
(4,1)
Out degree: (5,3)
==========inDegrees=============
(4,1)
(2,2)
(1,2)
(6,2)
(3,1)
In degree: (2,2)
(6,(6,(Fran,60)))
(3,(3,(Charlie,75)))
(2,(2,(Bob,37)))
(1,(1,(Alice,38)))
(5,(5,(Ed,65)))
(4,(4,(David,52)))
Edge(3,6,6)
Edge(2,1,14)
Edge(4,1,2)
Edge(5,6,6)
Edge(5,3,16)
Edge(3,2,8)
Edge(2,4,4)
Edge(5,2,4)
==========SubGraph=============
(6,(Fran,50))
(5,(Ed,55))
(3,(Charlie,65))
(4,(David,42))
Edge(5,3,8)
Edge(3,6,3)
Edge(5,6,3)
============连接操作==============
(3,User(Charlie,65,1,2))
(2,User(Bob,27,2,2))
(1,User(Alice,28,2,0))
(6,User(Fran,50,2,0))
(5,User(Ed,55,0,3))
(4,User(David,42,1,1))
(4,User(David,42,1,1))
(2,User(Bob,27,2,2))
找到5到各个顶点的最短距离
(1,5.0)
(2,2.0)
(3,8.0)
(4,4.0)
(5,0.0)
(6,3.0)

Process finished with exit code 0

运行截图如下:
在这里插入图片描述

Pregel API

图本身是递归数据结构,顶点的属性依赖于它们的邻居的属性,这些邻居的属性又依赖于自己的邻居的属性。所以需要重要的算法都是迭代的重新计算每个顶点的属性,直到满足某个确定的条件。
一系列的图并发抽象被提出来用来表达这些迭代算法。
GraphX公开了一个类似Pregel的操作

在这里插入图片描述

  • vprog:用户定义的顶点运行程序,它所用每一个顶点,负责接收进来的信息,并计算新的顶点值
  • sendMsg:发送消息
  • mergeMsg:合并消息

案例二:连通图算法

给定数据文件,找到存在的连通体

数据内容

自己生成一些即可:

1 2
1 3
2 4
3 4
4 5
5 6

编写代码

package icu.wzk

import org.apache.spark.graphx.{Graph, GraphLoader}
import org.apache.spark.{SparkConf, SparkContext}

object GraphExample2 {

  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
      .setAppName("GraphExample2")
      .setMaster("local[*]")
    val sc = new SparkContext(conf)
    sc.setLogLevel("WARN")

    // 从数据文件中加载 生成图
    val graph: Graph[Int, Int] = GraphLoader.edgeListFile(sc, "graph.txt")
    graph.vertices.foreach(println)
    graph.edges.foreach(println)
    // 生成连通图
    graph.connectedComponents()
      .vertices
      .sortBy(_._2)
      .foreach(println)

    // 关闭 SparkContext
    sc.stop()
  }
}

运行结果

(1,1)
(3,1)
(4,1)
(5,1)
(6,1)
(2,1)
Edge(1,2,1)
Edge(1,3,1)
Edge(2,4,1)
Edge(3,4,1)
Edge(4,5,1)
Edge(5,6,1)
(4,1)
(6,1)
(2,1)
(1,1)
(3,1)
(5,1)

运行截图如下所示:
在这里插入图片描述

案例三:寻找相同的用户,合并信息

需求明确

假设:

  • 假设五个不同信息可以作为用户标识,分别:1X,2X,3X,4X,5X
  • 每次可以选择使用若干为字段作为标识
  • 部分标识可能发生变化,如 12变为13 或 24变为25

根据以上规则,判断以下标识是否代表同一用户:

  • 11-21-32、12-22-33(X)
  • 11-21-32、11-21-52(OK)
  • 21-32、11-21-33(OK)
  • 11-21-32、32-48(OK)

问题:在以下数据中,找到同一个用户,合并相同用户的数据

  • 对于用户标识(id):合并后去重
  • 对于用户的信息:key相同,合并权重

编写代码

package icu.wzk

import org.apache.spark.graphx.{Edge, Graph, VertexId, VertexRDD}
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

object GraphExample3 {

  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
      .setAppName("GraphExample3")
      .setMaster("local[*]")
    val sc = new SparkContext(conf)
    sc.setLogLevel("WARN")

    val dataRDD: RDD[(List[Long], List[(String, Double)])] = sc.makeRDD(
      List(
        (List(11L, 21L, 31L), List("kw$北京" -> 1.0, "kw$上海" -> 1.0, "area$中关村" -> 1.0)),
        (List(21L, 32L, 41L), List("kw$上海" -> 1.0, "kw$天津" -> 1.0, "area$回龙观" -> 1.0)),
        (List(41L), List("kw$天津" -> 1.0, "area$中关村" -> 1.0)),
        (List(12L, 22L, 33L), List("kw$大数据" -> 1.0, "kw$spark" -> 1.0, "area$西二旗" -> 1.0)),
        (List(22L, 34L, 44L), List("kw$spark" -> 1.0, "area$五道口" -> 1.0)),
        (List(33L, 53L), List("kw$hive" -> 1.0, "kw$spark" -> 1.0, "area$西二旗" -> 1.0))
      )
    )

    // 1 将标识信息中的每一个元素抽取出来,作为ID
    // 备注1 这里使用了 flatMap 将元素压平
    // 备注2 这里丢掉了标签信息,因为这个RDD主要用于构造顶点、边
    // 备注3 顶点、边的数据要求Long,这个程序修改后才能用在我们的程序中
    val dotRDD: RDD[(VertexId, VertexId)] = dataRDD.flatMap {
      case (allids, _) => allids.map(id => (id, allids.mkString.hashCode.toLong))
    }

    // 2 定义顶点
    val vertexesRDD: RDD[(VertexId, String)] = dotRDD.map {
      case (id, _) => (id, "")
    }
    // 3 定义边(id: 单个标识信息:ids:全部的标识信息)
    val edgesRDD: RDD[Edge[Int]] = dotRDD.map {
      case (id, ids) => Edge(id, ids, 0)
    }
    // 4 生成图
    val graph = Graph(vertexesRDD, edgesRDD)
    // 5 找到强连通体
    val connectRDD: VertexRDD[VertexId] = graph.connectedComponents().vertices;
    // 6 定义中心点的数据
    val centerVertexRDD: RDD[(VertexId, (List[VertexId], List[(String, Double)]))] = dataRDD.map {
      case (allIds, tags) => (allIds.mkString.hashCode.toLong, (allIds, tags))
    }
    // 7 步骤5、6的数据做join 获取需要合并的数据
    val allInfoRDD = connectRDD.join(centerVertexRDD).map {
      case (_, (id2, (allIds, tags))) => (id2, (allIds, tags))
    }
    // 8 数据聚合(将同一个用户的标识、标签放在一起)
    val mergeInfoRDD: RDD[(VertexId, (List[VertexId], List[(String, Double)]))] = allInfoRDD
      .reduceByKey {
        case ((bufferList, bufferMap), (allIds, tags)) =>
          val newList = bufferList ++ allIds

          // map 合并
          val newMap = bufferMap ++ tags
          (newList, newMap)
      }

    // 9 数据合并(allIds去重,tags合并权重)
    val resultRDD: RDD[(List[VertexId], Map[String, Double])] = mergeInfoRDD.map {
      case (key, (allIds, tags)) =>
        val newIds = allIds.distinct
        val newTags = tags.groupBy(x => x._1).mapValues(lst => lst.map(x => x._2).sum)
        (newIds, newTags)
    }

    resultRDD.foreach(println)

    sc.stop()
  }

}

运行结果

(List(21, 32, 41, 11, 31),Map(area$中关村 -> 2.0, kw$北京 -> 1.0, kw$天津 -> 2.0, kw$上海 -> 2.0, area$回龙观 -> 1.0))
(List(22, 34, 44, 12, 33, 53),Map(kw$大数据 -> 1.0, kw$spark -> 3.0, area$五道口 -> 1.0, area$西二旗 -> 2.0, kw$hive -> 1.0))

运行的截图如下图:
在这里插入图片描述

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

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

相关文章

代码随想录 刷题记录-19 动态规划(3)完全背包理论、习题

一、完全背包理论 52. 携带研究材料 有N种物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品都有无限个&#xff08;也就是可以放入背包多次&#xff09;&#xff0c;求解将哪些物品装入背包里物品价值总和最大。 完…

(二) 初入MySQL 【数据库管理】

一、SQL语言分类 DDL&#xff1a;数据定义语言&#xff0c;用于创建数据库对象&#xff0c;如库、表、索引等 DML&#xff1a;数据操纵语言&#xff0c;用于对表中的数据进行管理,用来插入、删除和修改数据库中的数据 DQL&#xff1a;数据查询语言&#xff0c;用于从数据表中查…

Mybatis:基础巩固-DML

目录 一、概述二、新增三、修改四、删除 一、概述 DML语句主要用来完成对数据的增删改的操作。可以分为以下几类&#xff1a; 新增&#xff08;INSERT&#xff09;修改&#xff08;UPDATE&#xff09;删除&#xff08;DELETE&#xff09; 二、新增 INSERT INTO tb_user(name, …

【MySQL 14】用户管理

文章目录 &#x1f308; 一、用户管理⭐ 1. 用户信息⭐ 2. 创建用户⭐ 3. 修改密码⭐ 4. 删除用户 &#x1f308;二、数据库的权限⭐ 1. 权限列表⭐ 2. 查看权限⭐ 3. 授予权限⭐ 4. 回收权限 &#x1f308; 一、用户管理 MySQL 也会区分普通用户和超级用户。如果只是访问某一…

centos基础设置

1.设置网络 # 配置网络 vi /etc/sysconfig/network-scripts/ifcfg-ens33 #将里面的onboard设置为yes 2.修改计算机名 使用命令行&#xff1a; 打开终端。 使用 hostnamectl 命令可以直接修改主机名。例如&#xff0c;如果您想将主机名更改为 mynewhostname&#xff0c;您可以使…

selenium(一)基于java、元素定位

Selenium自动化 Selenium是一个用于Web应用程序的自动化测试工具。它直接运行在浏览器中&#xff0c;可以模拟用户在浏览器上面的行为操作。 chrome下载 https://www.google.com/chrome/ 下载的结果是 “ChromeSetup.exe”&#xff0c;双击该文件&#xff0c;安装程序会自…

selenium(二)基于java、元素操控、Frame切换、元素等待

元素操控 选择到元素之后&#xff0c;我们的代码会返回元素对应的 WebElement对象&#xff0c;通过这个对象&#xff0c;我们就可以 操控 元素 输入sendKeys 项目实战&#xff1a;在网银环境下载流水[交易明细]的时候&#xff0c;需要指定查询日期&#xff0c;有些银行是可以…

一个简单的CRM客户信息管理系统,提供客户,线索,公海,联系人,跟进信息和数据统计功能(附源码)

前言 在当今快速发展的商业环境中&#xff0c;企业管理面临着种种挑战&#xff0c;尤其是如何有效管理和维护客户关系成为了一个关键问题。现有的一些处理方案往往存在功能分散、操作复杂、数据孤岛等痛点&#xff0c;这不仅影响了工作效率&#xff0c;也难以满足企业对客户关…

仿华为车机功能之--修改Launcher3,实现横向滑动桌面空白处切换壁纸

本功能基于Android13 Launcher3 需求:模仿华为问界车机,实现横向滑动桌面空白处,切换壁纸功能(本质只是切换背景,没有切换壁纸)。 实现效果: 实现思路: 第一步首先得增加手势识别 第二步切换底图,不切换壁纸是因为切换壁纸动作太大,需要调用到WallpaperManager,耗…

【赵渝强老师】构建Docker Swarm集群

在了解了Docker Swarm的体系架构以后&#xff0c;基于已经部署好三台虚拟主机便可以非常容易地构建Swarm集群。下表列出了这三台虚拟机的相关信息。其中&#xff1a;master主机将作为Swarm Manager运行&#xff1b;而node1和node2将作为Swarm Node运行。 视频讲解如下&#xf…

nacos 动态读取nacos配置中心项目配置

实现了项目稳定运行情况下不需要在项目中改配置&#xff0c;直接在nacos中修改更方便。 pom文件&#xff1a; <!--读取bootstrap文件--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap…

大数据测试怎么做,数据应用测试、数据平台测试、数据仓库测试

本期内容由中通科技高级质量工程师龙渊在公益讲座中分享&#xff0c;他从大数据测试整体介绍、数据应用测试、数据平台测试以及数据仓库测试等方面&#xff0c;与大家共同探讨了大数据测试的方法实施与落地。 以下是讲座正文&#xff1a; 今天我们分享的内容主要从大数据简介…

阿里云Ubuntu系统安装/简单使用Kafka

一、安装kafka 1.下载安装包 1.1下载地址 https://kafka.apache.org/downloads 注意&#xff1a; 版本可以随意选择&#xff0c;我们选择版本为2.4.1 2.压缩文件上传/解压 2.1上传 2.2解压文件 #解压文件指令 tar -zxvf kafka_2.12-2.4.1.tgz -C /export/server/ #创建软…

py_gif制作

from PIL import Image import moviepy.editor as mp import os# 1. 定义函数 resize_frame&#xff0c;用于调整视频帧的大小 def resize_frame(frame, max_size240):img Image.fromarray(frame)img img.resize((max_size, max_size), Image.Resampling.LANCZOS) # 使用 LA…

【Java】—— Java面向对象基础:Java中如何创建和使用三角形类(TriAngle)来计算面积

目录 第一步&#xff1a;定义TriAngle类 第二步&#xff1a;编写TriAngleTest测试类 运行程序 总结 在Java编程中&#xff0c;面向对象的编程思想是一种非常强大的工具&#xff0c;它允许我们将现实世界中的实体&#xff08;如三角形&#xff09;抽象成程序中的类&#xff…

c++关于字符串的练习

提示并输入一个字符串&#xff0c;统计该字符串中字母个数、数字个数、空格个数、其他字符的个数 #include <iostream> #include<string> using namespace std;int main() {string s1;int letter0,digit0,space0,other0;cout<<"请输入一个字符串:"…

redis学习笔记 ——redis中的四大特殊数据结构

一.前言 在之前的学习中&#xff0c;我们已经介绍了Redis中常见的五种基本的数据结构&#xff0c;而今天我们就要开始介绍Redis的四种特殊的数据结构&#xff0c;它们分别是bitmap(位图)&#xff0c; HyperLogLog(基数统计),Geospatial(地理信息),Stream。 二.位图(Bitmap) …

springboot+vue+mybatis计算机毕业设计电子产品交易系统+PPT+论文+讲解+售后

系统根据现有的管理模块进行开发和扩展&#xff0c;采用面向对象的开发的思想和结构化的开发方法对电子产品交易管理的现状进行系统调查。采用结构化的分析设计&#xff0c;该方法要求结合一定的图表&#xff0c;在模块化的基础上进行系统的开发工作。在设计中采用“自下而上”…

并发高负载场景下的负载均衡优化方案

并发高负载场景下&#xff0c;负载均衡优化技术 负载均衡是一种计算机技术&#xff0c;主要用于在多个计算机(如计算机集群)、网络连接、CPU、硬盘驱动器或其他资源中分配工作负载。 其主要目标是优化资源使用、最大化吞吐率、最小化响应时间&#xff0c;同时避免任何一个资源的…

跨境电商避坑指南:如何在亚马逊和速卖通安全进行测评补单

大家好&#xff0c;近期&#xff0c;不少从事跨境电商的朋友纷纷向我求助&#xff0c;他们在执行测评补单时频繁遭遇支付难题&#xff0c;如支付失败、支付成功后订单被取消&#xff0c;更有甚者&#xff0c;账号遭遇封禁。许多朋友误以为这仅仅是支付卡的问题&#xff0c;但实…