Go底层原理:一起来唠唠GMP调度(一)

news2024/9/21 12:29:57

目录

  • 前言
  • 一、进程、线程、Goroutine
    • 1、进程与线程
    • 2、Goroutine
  • 二、Go调度器设计思想
    • 1、线程模型
      • 1.1 内核级线程模型
      • 1.2 用户级线程模型
      • 1.3 混合型线程模型
    • 2、 被废弃的 G-M 调度器
      • 2.1 了解 G-M 调度如何工作
    • 3、如今高效的 GMP 模型
      • 3.1 GMP模型调度流程
      • 3.2 GMP调度设计策略
      • 3.3 GMP调度场景
        • 3.3.1 G1 运行中创建 G2
        • 3.3.2 G 运行结束后
        • 3.3.3 M 上创建的 G 个数大于本地队列长度时
        • 3.3.5 M 的自旋状态
        • 3.3.5 任务窃取机制
        • 3.3.6 G 发生系统调用进入阻塞时
          • 3.3.6.1 用户态阻塞
          • 3.3.6.2 系统调用阻塞
        • 3.3.7 G 退出系统调用时

前言

春招开始了,作为23届的科班咸鱼学习记录一下八股文和go底层原理(GC、GMP调度、goroutine等)本文介绍 Go 语言运行时调度器的实现原理,其中包含调度器的设计与实现原理、演变过程以及与运行时调度相关的数据结构。
参考几篇不错的文章:
mingguangtu《深入分析Go1.18 GMP调度器底层原理》
刘丹冰Aceld《Golang 调度器 GMP 原理与调度全分析》

一、进程、线程、Goroutine

1、进程与线程

在了解Go的调度器的时候,都离不开操作系统、进程与线程这些概念。

进程线程
操作系统资源调度和分配的基本单位CPU调度和分派的基本单位
一个进程可以有多个线程一个线程只能属于一个进程
有独立的地址空间同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器
创建销毁开销大,进程切换时,耗费资源较大创建销毁开销小,线程切换时,耗费资源小
实现操作系统的并发实现进程内部的并发

进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。资源分配给进程,同一进程的所有线程共享该进程的所有资源。同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量。

在同一进程中,线程的切换不会引起进程切换。在不同进程中进行线程切换,如从一个进程内的线程切换到另一个进程中的线程时,会引起进程切换。

在这里插入图片描述

摘抄某知乎大佬的比喻:进程=火车,线程=车厢

  • 线程在进程下行进(单纯的车厢无法运行)
  • 一个进程可以包含多个线程(一辆火车可以有多个车厢)
  • 不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)
  • 同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)
  • 进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)
  • 进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢)
  • 进程可以拓展到多机,进程最多适合多核(不同火车可以开在多个轨道上,同一火车的车厢不能在行进的不同的轨道上)
  • 进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。(比如火车上的洗手间)-“互斥锁”
  • 进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量”

2、Goroutine

Goroutine = Golang + Coroutine。Goroutine是golang实现的用户态、轻量级的协程。Goroutine具有以下特点:

  • 相比线程,其启动的代价很小,以很小栈空间启动(2Kb左右)
  • 能够动态地伸缩栈的大小,最大可以支持到Gb级别
  • 工作在用户态,切换成本很小
  • 与线程关系是 N:M,即可以在 N 个系统线程上多工调度 M 个Goroutine

Goroutine是Golang支持高并发的重要保障。Golang可以创建成千上万个Goroutine来处理任务,将这些Goroutine分配、负载、调度到处理器上采用的是G-M-P模型。

二、Go调度器设计思想

在我们学习Go调度器前,先了解线程模型的概念,大部分的调度器都是基于线程模型思想开发的。

1、线程模型

线程创建、管理、调度等采用的方式称为线程模型。线程模型一般分为以下三种:

  • 内核级线程(Kernel Level Thread)模型
  • 用户级线程(User Level Thread)模型
  • 两级线程模型,也称混合型线程模型

三大线程模型最大差异就在于用户级线程与内核调度实体KSE(KSE,Kernel Scheduling Entity)之间的对应关系。KSE是Kernel Scheduling Entity的缩写,其是可被操作系统内核调度器调度的对象实体,是操作系统内核的最小调度单元,可以简单理解为内核级线程。

用户级线程即协程,由应用程序创建与管理,协程必须与内核级线程绑定之后才能执行。线程由 CPU 调度是抢占式的,协程由用户态调度是协作式的,一个协程让出 CPU 后,才执行下一个协程。

特性用户级线程内核级线程
创建者应用程序内核
操作系统是否感知存在
开销成本创建成本低,上下文切换成本低,上下文切换不需要硬件支持创建成本高,上下文切换成本高,上下文切换需要硬件支持
如果线程阻塞整个进程将被阻塞。即不能利用多处理来发挥并发优势其他线程可以继续执行,进程不会阻塞
案例JAVA threadsWindow Solaris

1.1 内核级线程模型

内核级线程模型中用户线程与内核线程是一对一关系(1 : 1)。线程的创建、销毁、切换工作都是有内核完成的。应用程序不参与线程的管理工作,只能调用内核级线程编程接口(应用程序创建一个新线程或撤销一个已有线程时,都会进行一个系统调用)。

操作系统调度器管理、调度并分派这些线程。运行时调度器为每个用户级线程静态关联一个内核级线程,用户线程在其生命期内都会绑定到该内核线程。一旦用户线程终止,两个线程都将离开系统。

在这里插入图片描述
大部分编程语言的线程库(如linux的pthread,Java的java.lang.Thread,C++11的std::thread等等)都是对操作系统的线程(内核级线程)的一层封装,创建出来的每个线程与一个不同的内核级线程静态关联,因此其调度完全由OS调度器来做。

内核级线程模型有如下优点:

  • 在多处理器系统中,内核能够并行执行同一进程内的多个线程
  • 如果进程中的一个线程被阻塞,不会阻塞其他线程,是能够切换同一进程内的其他线程继续执行
  • 当一个线程阻塞时,内核根据选择可以运行另一个进程的线程,而用户空间实现的线程中,运行时系统始终运行自己进程中的线程

缺点:

  • 线程的创建与删除都需要CPU参与,成本大
  • 需要使用大量线程的场景下对OS的性能影响会很大

1.2 用户级线程模型

用户线程模型中的用户线程与内核线程是多对一关系(N : 1)。线程的创建、销毁以及线程之间的协调、同步等工作都是在用户态完成,具体来说就是由应用程序的线程库来完成。线程的并发处理从宏观来看,任意时刻每个进程只能够有一个线程在运行,且只有一个处理器内核会被分配给该进程。

从图中可以看出来:一个进程中所有创建的线程都与同一个内核线程在运行时动态关联。内核线程将被操作系统调度器指派到处理器内核。用户级线程是一种”多对一”的线程映射

在这里插入图片描述

用户级线程有如下优点:

  • 创建和销毁线程、线程切换代价等线程管理的代价比内核线程少得多, 因为保存线程状态的过程和调用程序都只是本地过程
  • 线程能够利用的表空间和堆栈空间比内核级线程多

缺点:

  • 线程发生I/O或页面故障引起的阻塞时,如果调用阻塞系统调用则内核由于不知道有多线程的存在,而会阻塞整个进程从而阻塞所有线程, 因此同一进程中只能同时有一个线程在运行
  • 资源调度按照进程进行,多个处理机下,同一个进程中的线程只能在同一个处理机下分时复用

1.3 混合型线程模型

混合型线程模型中用户线程与内核线程是多对多关系(N : M)。混合型线程模型充分吸收上面两种模型的优点,为一个进程中创建多个内核线程,并且线程可以与不同的内核线程在运行时进行动态关联,当某个内核线程由于其上工作的线程的阻塞操作被内核调度出CPU时,当前与其关联的其余用户线程可以重新与其他内核线程建立关联关系。

其线程创建在用户空间中完成,线程的调度和同步也在应用程序中进行。一个应用程序中的多个用户级线程被绑定到一些(小于或等于用户级线程的数目)内核级线程上。

此模型有时也被称为 两级线程模型,即用户调度器实现用户线程到内核线程的“调度”,内核调度器实现内核线程到CPU上的调度。

在这里插入图片描述

缺点:

  • 这种动态关联机制的实现很复杂,也需要用户自己去实现。Go语言中的并发就是使用的这种实现方式,Go为了实现该模型自己实现了一个运行时调度器来负责Go中的"线程"与内核的动态关联。

这里我们只需要知道 Go 的线程模型,下面我们讲一下GMP调度的升级之路
在这里插入图片描述

2、 被废弃的 G-M 调度器

Golang在底层实现了混合型线程模型。Go 语言将线程分为了两种类型:内核级线程 M (Machine),轻量级的用户态的协程 Goroutine。

这里我们提到了 Go 语言调度器的三个核心概念中的两个:

  • M: Machine的缩写,代表了内核线程 OS Thread,CPU调度的基本单元

  • G: Goroutine的缩写,用户态、轻量级的协程,一个 G 代表了对一段需要被执行的 Go 语言程序的封装;每个 Goroutine 都有自己独立的栈存放自己程序的运行状态;分配的栈大小 2KB,可以按需扩缩容

在这里插入图片描述

2.1 了解 G-M 调度如何工作

老调度器存在一个全局 G 队列,M在执行和放回队首的 G 都需要访问全局 G 队列,因为存在多个 M 同时竞争,在访问临界资源全局 G 队列时需要加锁进行保证互斥 / 同步,所以全局 G 队列是有互斥锁进行保护的。
在这里插入图片描述
我们知道在多线程下,老调度器存在几个缺点:

  • 创建、销毁、调度 G 都需要每个 M 获取锁,在多线程下这就形成了激烈的锁竞争
  • M 转移 G 会造成延迟和额外的系统负载。比如当 G 中包含创建新协程的时候,M 创建了 G’,为了继续执行 G,需要把 G’交给 M’执行,也造成了很差的局部性,因为 G’和 G 是相关的,最好放在 M 上执行,而不是其他 M’。
  • 系统调用 (CPU 在 M 之间的切换) 导致频繁的线程阻塞和取消阻塞操作增加了系统开销。

3、如今高效的 GMP 模型

Go为了解决 G-M 调度的缺点,在原先的 G-M 调度器基础上引进了 P,也就是如今Go优秀的GMP 调度模型。

  • P:Processor的缩写,代表一个虚拟的处理器,是线程 M 和 G 的中间层,它能提供线程需要的上下文环境,也会负责调度线程上的等待队列,它维护一个本地的可运行的 G 队列,通过处理器 P 的调度,每一个内核线程都能够执行多个 Goroutine,它能在 Goroutine 进行一些 I/O 操作时及时让出计算资源,提高线程的利用率。

在这里插入图片描述

3.1 GMP模型调度流程

了解一下图中出现的几个概念:

  1. 全局队列(Global Queue):存放等待运行的 G。
  2. P 的本地队列:同全局队列类似,存放的也是等待运行的 G,本地队列中 G 的数量有限,不超过 256 个。新建 G’时,G’优先加入到 P 的本地队列,如果队列满了,则会把本地队列中一半的 G 移动到全局队列(本地队列的前一半 + G’,通常为 G’)。

在这里插入图片描述

GMP调度流程大致如下:

  1. 线程M想运行任务就需得获取 P,即与P关联
  2. P 的本地队列(LRQ)获取 G
  3. 若LRQ中没有可运行的G,M 会尝试从全局队列(GRQ)拿一批G放到P的本地队列(拿取数量 = 全局队列中 G 的数量 / 运行的 M-P 数量 + 1,至少拿一个,保证合理分配)
  4. 若全局队列也未找到可运行的G时候,M会随机从其他 P 的本地队列偷一半放到自己 P 的本地队列
  5. 拿到可运行的G之后,M 运行 G,G 执行结束后,M 会从 P 获取下一个 G,不断重复下去
  6. 当 M 执行某一个 G 时候如果发生了 syscall 或则其余阻塞操作,M 会阻塞,如果当前有一些 G 在执行,runtime 会把这个线程 M 从 P 中摘除 (detach),然后再创建一个新的操作系统的线程 (如果有空闲的线程可用就复用空闲线程) 来服务于这个 P
  7. 当 M 系统调用结束时候,这个 G 会尝试获取一个空闲的 P 执行,并放入到这个 P 的本地队列。如果获取不到 P,那么这个线程 M 变成休眠状态, 加入到空闲线程中,然后这个 G 会被放入全局队列中

3.2 GMP调度设计策略

  • 复用线程:避免频繁的创建、销毁线程,而是对线程的复用。可以使用下面两种机制实现对线程对复用。

    • Work Stealing 任务窃取机制:M 优先执行其所绑定的 P 的本地队列的 G;如果本地队列为空,当 P 本地队列为空时,M 也会尝试从全局队列拿一批 G 放到 P 的本地队列;如果全局队列也为空时,会从其他 P 的本地队列偷一半放到自己 P 的本地队列,这种 GMP 调度模型也叫任务窃取调度模型;

    • Hand Off 交接机制:当前线程 M 因为 G 进行系统调用阻塞时,线程 M 释放绑定的 P,P 会寻找其他空闲的线程 M 绑定执行, 如果没找到空闲的 M 的 P 进入全局 P 队列。

  • 利用多核并行能力:Go默认设置线程 M 最大数量为10000,实际等于 CPU 的核心数。GOMAXPROCS 设置 P 处理器的数量,实际上 P 数量应该小于等于 CPU 核数。GOMAXPROCS 也限制了并行,比如 GOMAXPROCS = 核数/2,则最多利用了一半的 CPU 核进行并行。

  • 基于协作的抢占机制

    • G 主动让出 CPU 才能调度执行下一个 G,某些 Goroutine 可以长时间占用线程,造成其它 Goroutine 的饥饿;
    • 垃圾回收需要暂停整个程序(Stop-the-world,STW),最长可能需要几分钟的时间,导致整个程序无法工作;
    • Go为了公平,让每个孩子都有奶吃,一个 G 最多占用 CPU 10ms,防止其他 G 等待时间过长被饿死。
  • 基于信号的真抢占机制:尽管基于协作的抢占机制能够缓解长时间 GC 导致整个程序无法工作和大多数 Goroutine 饥饿问题,但是还是有部分情况下,Go调度器有无法被抢占的情况,例如,for 循环或者垃圾回收长时间占用线程,为了解决这些问题, Go1.14 引入了基于信号的抢占式调度机制,能够解决 GC 垃圾回收和栈扫描时存在的问题。

3.3 GMP调度场景

可能到这里对于调度流程和设计策略还是一知半解,先用几张图形象的描述下 GMP 调度机制的一些场景,帮助理解 GMP 调度器为了保证公平性、可扩展性、及提高并发效率,所设计的一些机制和策略。

3.3.1 G1 运行中创建 G2

正在 M1 上运行的 G1,通过go func() 创建 G2 后,由于局部性,G2优先放入 P 的本地队列。
在这里插入图片描述

3.3.2 G 运行结束后

M1 上的 G1 运行完成后(调用goexit()函数),M1 上运行的 Goroutine 会切换为 G0,G0 负责调度协程的切换(运行schedule() 函数),从 M1 上 P 的本地运行队列获取 G2 去执行(函数execute())。

注意:这里 G0 是程序启动时的线程 M(也叫M0)的系统栈表示的 G 结构体,负责 M 上 G 的调度;

在这里插入图片描述

3.3.3 M 上创建的 G 个数大于本地队列长度时

如果 P 本地队列最多能存 4 个 G(实际上是256个),正在 M1 上运行的 G2 要通过 go func()创建 6 个 G,那么,前 4 个 G 放在 P 本地队列中,G2 创建了第 5 个 G(G7)时,P 本地队列中前一半和 G7 一起打乱顺序放入全局队列,P 本地队列剩下的 G 往前移动,G2 创建的第 6 个 G(G8)时,放入 P 本地队列中,因为还有空间

在这里插入图片描述

3.3.5 M 的自旋状态

创建新的 G 时,运行的 G 会尝试唤醒其他空闲的 M 绑定 P 去执行,如果 G2 唤醒了M2,M2 绑定了一个 P2,会先运行 M2 的 G0,这时 M2 没有从 P2 的本地队列中找到 G,会进入自旋状态(spinning),自旋状态的 M2 会尝试从全局空闲线程队列里面获取 G,放到 P2 本地队列去执行,获取的数量满足公式:n = min(len(globrunqsize)/GOMAXPROCS + 1, len(localrunsize/2)),含义是每个P应该从全局队列承担的 G 数量,为了公平性和提高效率,不能太多,要给其他 P 留点;

在这里插入图片描述

3.3.5 任务窃取机制

自旋状态的 M 会寻找可运行的 G,如果全局队列为空,则会从其他 P 偷取 G 来执行,个数是其他 P 运行队列的一半;

在这里插入图片描述

3.3.6 G 发生系统调用进入阻塞时

如果 G 发生系统调度进入阻塞,其所在的 M 也会阻塞,因为会进入内核状态等待系统资源,和 M 绑定的 P 会解除绑定寻找空闲的 M 执行,这是为了提高效率,不能让 P 本地队列的 G 因所在 M 进入阻塞状态而无法执行。
在这里插入图片描述

GMP模型的阻塞可能发生在下面几种情况:

  • I/O,select
  • block on syscall
  • channel
  • 等待锁
  • runtime.Gosched()
3.3.6.1 用户态阻塞
  • 当 G 因为channel操作或者network I/O而阻塞时(实际上golang已经用netpoller实现了 G 网络I/O阻塞不会导致M被阻塞,仅阻塞G),对应的G会被放置到某个wait队列(如channel的waitq),该G的状态由_Gruning变为_Gwaitting,而M会跳过该G尝试获取并执行下一个G,如果此时没有runnable的G供M运行,那么M将解绑P,并进入sleep状态;
  • 当阻塞的G被另一端的G2唤醒时(比如channel的可读/写通知),G被标记为runnable,尝试加入G2所在P的runnext,然后再是P的Local队列和Global队列。
3.3.6.2 系统调用阻塞
  • 当G被阻塞在某个系统调用上时,此时G会阻塞在_Gsyscall状态,M也处于 block on syscall 状态,此时的M执行可被抢占调度:执行该G的M会与P解绑,而P则尝试与其它idle的M绑定,继续执行其它G。如果没有其它idle的M,但P的Local队列中仍然有G需要执行,则创建一个新的M;
  • 当系统调用完成后,G会重新尝试获取一个idle的P进入它的Local队列恢复执行,如果没有idle的P,G会被标记为runnable加入到Global队列。

3.3.7 G 退出系统调用时

如果刚才进入系统调用的 G2 解除了阻塞,其所在的 M1 会寻找 P 去执行,优先找原来的 P,发现没有找到或者 P 已经与其他 M 绑定,则其上的 G2 会进入全局队列,等其他 M 获取执行,M1 进入休眠线程队列,等待 P 将他重新唤醒。
在这里插入图片描述

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

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

相关文章

【Vue3】封装数字框组件

数量选择组件-基本结构 &#xff08;1&#xff09;准备基本结构 <script lang"ts" setup name"Numbox"> // </script> <template><div class"numbox"><div class"label">数量</div><div cla…

C语言-基础了解-19-C位域

C位域 一、C位域 如果程序的结构中包含多个开关量&#xff0c;只有 TRUE/FALSE 变量&#xff0c;如下&#xff1a; struct {unsigned int widthValidated;unsigned int heightValidated; } status;这种结构需要 8 字节的内存空间&#xff0c;但在实际上&#xff0c;在每个变…

引领云数仓创新浪潮 HashData闪耀PostgreSQL中国技术大会

3月3日-3月5日&#xff0c;第12届PostgreSQL中国技术大会在杭州举行。本次大会以“突破•进化•共赢——安全可靠&#xff0c;共建与机遇”为主题&#xff0c;以线上线上结合的方式&#xff0c;邀请了PG领域众多行业大咖、学术精英及技术专家&#xff0c;共同探讨数据库未来的发…

【C/C++ 数据结构】-八大排序之 归并排序其它排序

作者&#xff1a;学Java的冬瓜 博客主页&#xff1a;☀冬瓜的主页&#x1f319; 专栏&#xff1a;【C/C数据结构与算法】 分享&#xff1a;本王在此&#xff0c;狼狈为奸者&#xff0c;谋权篡位者&#xff0c;倒行逆施者&#xff0c;都得死&#xff01; ——岐王李茂贞《画江湖…

Linux 静态与动态编译、静态库与动态库

文章目录一、库的简介二、静态链接和动态链接1、静态链接2、动态链接3、GCC 下动态库与静态库三、静态库制作和使用四、动态库制作和使用总结一、库的简介 什么是库文件呢&#xff1f; 所谓库文件&#xff0c;大家可以将其等价为压缩包文件&#xff0c;该文件内部通常包含不止…

MyBatis源码分析(六)MetaObject工具类的使用与源码分析

文章目录一、MetaObject基本使用二、关键类源码分析1、MetaObject的构造方法2、PropertyTokenizer分词器3、BeanWrapper4、MetaClass5、DefaultReflectorFactory6、Reflector7、总结三、MetaObject的getValue源码分析写在后面一、MetaObject基本使用 public class User {priva…

OPT(奥普特)一键测量传感器SmartFlash高精度的四重保证

OPT&#xff08;奥普特&#xff09;一键测量传感器SmartFlash集成了机器视觉的边缘提取、自动匹配、自动对焦、自动学习及图像合成等人工智能技术&#xff0c;采用双远心光路及多角度照明系统设计&#xff0c;搭载高精度运动平台&#xff0c;并通过亚像素边缘提取算法处理图像&…

Mysql全解[中级篇]

目录存储引擎MySQL体系结构1). 连接层2). 服务层3). 引擎层4). 存储层存储引擎介绍存储引擎特点InnoDBMyISAMMemory文件区别及特点存储引擎选择索引无索引情况有索引情况特点索引结构二叉树红黑树B-TreeBTreeMySQL中优化之后的BTreeHash索引分类聚集索引&二级索引回表查询索…

双周赛99(贪心、数学、区间合并计算、换根DP)

文章目录双周赛99[6312. 最小和分割](https://leetcode.cn/problems/split-with-minimum-sum/)贪心[6311. 统计染色格子数](https://leetcode.cn/problems/count-total-number-of-colored-cells/)找规律[6313. 统计将重叠区间合并成组的方案数](https://leetcode.cn/problems/c…

规并排序(Swift版本)

Overview 概述 时间复杂度为 O(nlogn) ;适合大规模的数据排序 ;相比于冒泡排序、插入排序、选择排序这三种排序算法, 更加常用 ;用到了分治思想(即分而治之, 英文叫 “Divide and conquer”)&#xff0c;非常巧妙 ;英文名称: Merge Sort ; 分治思想, 在很多领域都有广泛的应用…

windows系统安装Linux虚拟机教程

虚拟机的安装首先要下载虚拟机的安装包&#xff0c;当前最新版本是VMware 16.2.1。软件我都已经给大家准备好了&#xff08;含序列号&#xff09;&#xff0c;大家在这里下载就好。虚拟机安装包下载完毕之后&#xff0c;将它安装到电脑里。这个安装过程很简单&#xff0c;一路下…

Linux操作系统学习(线程池)

文章目录线程池线程池原理代码示例单例模式饿汉模式懒汉模式饿汉懒汉对比其他的锁线程池 线程池原理 ​ 线程池是一种线程使用模式。在多线程应用中&#xff0c;若每有一个任务&#xff0c;线程就去调度相应的函数去创建&#xff0c;当任务过多时&#xff0c;每次都去调度且每…

CCF大数据专家委员会十周年纪念庆典纪实:拥抱数字时代,展望科技未来

山河远阔&#xff0c;奋进十年&#xff0c;作为国内大数据领域最权威的学术组织&#xff0c;CCF大数据专家委员会&#xff08;以下简称“大专委”&#xff09;不忘初心&#xff0c;凝心聚力&#xff0c;见证并推动了过去10年来大数据技术生态在中国的建立、发展和成熟。 2023年…

HBase安装

文章目录一、安装Zookeeper二、安装HBase三、启动Hbase步骤四、关闭进程顺序五、简单使用Hbase在开始安装HBase之前&#xff0c;请确保您已经安装了Java运行环境和Hadoop分布式文件系统。如果您还没有安装这些软件&#xff0c;请查看之前博文介绍安装。 HBase安装包&#xff1a…

谷歌广告投放步骤流程是什么?一文带你全方位了解实操细节

谷歌&#xff0c;大家都不陌生吧&#xff0c;一个人们很常用的搜索引擎。而谷歌还可以打广告&#xff0c;即谷歌广告&#xff0c;那这跟跨境电商有什么关心呢&#xff1f;东哥告诉大家&#xff0c;关系大了去了&#xff0c;毕竟如果用户搜索与我们相关的关键词&#xff0c;就有…

streaming systems 第二章

The What, Where, When, and How of Data Processing 第一章主要关注三个领域:术语&#xff0c;准确定义我使用重载术语时的意思&#xff0c;如“流”;批处理和流处理&#xff0c;比较两种类型系统的理论能力&#xff0c;并假设使流处理系统超越批处理系统只有两件事是必要的:…

【Netty】第一章 NIO 三大组件、ByteBuffer 和文件编程

【Netty】第一章 NIO 三大组件、ByteBuffer 和文件编程 文章目录【Netty】第一章 NIO 三大组件、ByteBuffer 和文件编程一、Channel & Buffer二、Selector三、ByteBuffer1.ByteBuffer 使用方式2.ByteBuffer 结构3.ByteBuffer 常用方法4.Scattering Reads4.Gathering Write5…

Java SPI机制了解与应用

1. 了解SPI机制 我们在平时学习和工作中总是会听到Java SPI机制&#xff0c;特别是使用第三方框架的时候&#xff0c;那么什么是SP机制呢&#xff1f;SPI 全称 Service Provider Interface&#xff0c;是 Java 提供的一套用来被第三方实现或者扩展的接口&#xff0c;它可以用来…

【java】Java连接mysql数据库及mysql驱动jar包下载和使用

文章目录JDBCJDBC本质&#xff1a;JDBC作用&#xff1a;跟数据库建立连接发送 SQL 语句返回处理结果操作流程和具体的连接步骤如下&#xff1a;操作步骤&#xff1a;需要导入驱动jar包 mysql-connector-java-8.0.22.jar注册驱动获取数据库连接对象 Connection定义sql获取执行sq…

第十届CCF大数据与计算智能大赛总决赛暨颁奖典礼在苏州吴江顺利举办

2月24日-25日&#xff0c;中国计算机学会&#xff08;CCF&#xff09;主办、苏州市吴江区人民政府支持&#xff0c;苏州市吴江区工信局、吴江区东太湖度假区管理办公室、苏州市吴江区科技局、CCF大数据专家委员会、CCF自然语言处理专业委员会、CCF高性能计算专业委员会、CCF计算…