从Clickhouse 到 Snowflake: 云原生

news2025/1/17 0:14:33

云原生Clickhouse优势概述

以Clickhouse为基础,借鉴Snowflake等系统的设计思路,打造一款高性能的云原生OLAP系统,为用户提供多场景下的一站式的数据分析平台。

  • 简单、易维护:集群管理、统一共享分布式调度服务
  • 高可用、可扩展:支持500万以上的Table
  • 低成本:存储成本至少降低了50%
  • 兼容开源,复用超高性能:兼容协议、语法、数据库存储格式

Clickhouse是一款性能十分强悍的OLAP引擎,凭借优秀的性能在用户行为分析、ABTest、在线报表等多个领域大放异彩,但是目前Clickhouse在易用性、稳定性、可维护性、功能特性等方面都还有较多不足,主要体现在以下几方面:

  • 易用性方面,需要用户感知本地表、分布式表、Zookeeper等概念,在建表、导入、查询等流程中都有涉及,后期Schema Change时也需要分别做处理,使用门槛较高;
  • 稳定性方面,重度依赖Zookeeper,Zookeeper已经成为明显的中心化瓶颈,尤其在多副本场景下单机的写入、Merge、DDL的执行都需要经过Zookeeper协调,一旦Zookeeper不稳定或者代码有Bug,整个分布式集群都会不稳定;
  • 可维护性方面,缺少节点加入、节点退出、副本均衡等必要的分布式管理功能,一旦集群数量变多,维护代价巨大;
  • 功能特性方面,没有真正的MPP查询层,在分布式Join、聚合等方面能力不足,语法上也不符合相关标准,有一定的学习成本;

Snowflake 是当下最火的云原生数仓,它SaaS化的设计理念极大的提升了数据开发者的使用体验,Clickhouse高性能的存储引擎和计算算子是一个非常优秀的底座,我们希望以Clickhouse为基础,借鉴Snowflake的设计思路,打造一款高性能的云原生OLAP系统,为用户提供多场景下的一站式的数据分析平台,包括:

  • HTAP 场景,与腾讯CDB、CynosDB等无缝集成,实时同步OLTP系统中的变更,基于列存和并行执行引擎,实现海量数据下的亚秒级分析;同时支持MySQL协议和语法,无缝兼容用户现有的工具,达到零迁移成本。
  • 数仓场景,支持用户数据实时入仓,为用户提供超高性能的在线分析服务;针对结构化、半结构化数据,实现统一的分析解决方案,利用Clickhouse丰富可扩展的算子,构建简单易用的流式处理框架简化ETL流程。
  • 数据湖场景,支持对象存储COS、CFS、Elasticsearch、MongoDB等多数据源的查询,通过跟多个数据源做深度的整合,利用高速的向量化执行引擎,提升多源数据的分析能力及性能。

 

1、云原生Clickhouse架构设计

存算分离正在数据库领域掀起一场变革,不论OLTP还是OLAP系统都在拥抱存算分离。通过对Clickhouse的源码进行阅读分析,发现在Clickhouse中一旦实现了存算分离,那么稳定性、可维护性以及成本等方面的问题都会迎刃可解。Clickhouse虽然在分布式集群管理方面很弱,但是这也意味着在改造存算分离架构时的负担很少,成为了一个巨大的优势,所以我们把存算分离作为第一个目标,希望通过引入存算分离技术,并且围绕存算分离技术对分布式集群管理等各个方面进行改造,为用户提供一个更好的Clickhouse。

云原生Clickhouse的架构设计如下图,具体包括三层:

(云原生Clickhouse架构设计图)

集群管理层:分布式集群的大脑,它主要包含基于分布式一致性协议实现的元数据管理服务、多集群共享的分布式任务调度服务;

计算层:用户通过创建的计算集群来实际使用分析服务,每个计算集群由多个节点组成,用户的查询任务在一个计算集群上的节点里完成,同一个用户的多个计算集群可共享集群管理层;

存储层:基于共享存储实现,用户的所有数据都存放在共享存储内,可以被多个计算集群访问,同时它提供了廉价、按需、无限扩展的存储能力;

数据流

  • 用户直接连接Clickhouse Node,而不是Master Node,所以可以通过现有的Clickhouse工具连接和使用;
  • 数据流在多个Clickhouse Node之间传输,不会经过Master Node,所以不会成为中心化瓶颈;

控制流

  • 控制流不再依赖Zookeeper来协调,而是通过一个全局共享的Master Node来实现;
  • 所有的分布式DDL命令都转发给Master Node,由Master Node协调DDL任务在各个节点上的执行,Master控制DDL的并发、失败处理;
  • Master Node存储了全局的、统一的Schema信息,为后续的MPP查询层打下了元数据的基础;
  • Master Node控制节点加入和退出等集群管理工作;
  • 由于Master服务是非常轻量的,所以可以在多个集群之间共享,能有效的降低部署Zookeeper带来的成本开销;

基于共享存储的存算分离机制

  • 强一致,数据存放到共享存储上,各个节点可以有一致的数据视图,任何一个节点写入数据,其他副本均立即可见;
  • 基于共享存储我们实现了Insert、Mutation、Alter等任务的冲突处理机制;
  • Merge/Mutation过程可以在任意一个副本上执行,多个节点可以Merge不同的Part,在高速导入的情况下,能加速Merge的速度,快速的减少底层小文件数量;

2、云原生Clickhouse核心特性

2.1 易使用,易运维

过去在运维一个Clickhouse集群时,很头疼的一点就是集群管理,例如,我们要向集群中增加一个节点,需要以下操作:

  • 启动新的节点;
  • 在新节点上创建本地表和分布式表,为了获取已创建的表信息,就需要到其他节点上查询,如果表比较多就需要脚本来配合,相信运维Clickhouse的同学都积累了不少脚本-;
  • 修改所有节点的配置文件Metrica.xml,把新节点信息写入,生效;

在新架构下,Master维护了全局统一的元数据信息,使得我们可以通过一条条简单的SQL命令来自动化集群管理, 例如下面这个命令就是向集群中增加一个副本节点:

ALTER CLUSTER cluster_name ADD BACKEND 'ip:port' TO SHARD  2;

这个命令首先会修改Master统一管理的元数据,然后新增的Clickhouse Node会从Master上同步相关元数据,更新本地配置,最终用户可以在Clickhouse上运行以下命令来获取集群的节点信息,同时当前Clickhouse的各种查询语句也可以继续使用集群的拓扑结构来构建复杂的查询:

SELECT * FROM SYSTEM.CLUSTERS;

此外,用户不需要再使用ReplicatedMergeTree引擎,不需要关注元数据在Zookeeper上的存储路径,只需要普通的MergeTree引擎即可,系统内部会自动为每个表分配唯一的数据存储路径,并且使互为副本的多个表共用存储空间。

CREATE TABLE t1 (
	partition_col_1 String,
	tc1 int,
	tc2 int)
ENGINE=MergeTree() 
PARTITION BY partition_col_1 
ORDER BY tc1;

2.2 统一共享的分布式任务调度服务

(分布式DDL任务执行流程图)

在引入Master Node后,分布式DDL任务的执行流程如上图所示,下面以Create Table为例介绍一下具体的流程:

  • 用户连接任意一个Clickhouse Node发送Create Table on cluster 请求,该节点做初步的解析后,把这个命令以一个DDL Job的形式发送给Master;
  • Master Node做DDL Job的解析,把Job转化为需要发送给集群内所有Clickhouse Node的DDL Task,每一个Task都是建立Local Table;
  • Clickhouse Node执行完DDL Task之后给Master返回成功消息;
  • Master做元数据的持久化,并且给客户端Clickhouse Node1 返回成功消息;

基于Master的分布式架构还具备以下特点:

高可用:Master Node自身多副本,多副本之间通过一致性协议保证高可用。同时,我们考虑了Master Node和ClickHouse Node间的松耦合设计,即使Master Node全部故障,也不影响存量业务的普通读写操作,仅限制新DDL操作的执行。

并发控制:Master Node能够区分对不同Table的DDL请求,可以控制不同请求的并发级别,比如对于Alter是顺序执行,对于Mutation可以并发执行。

回滚机制:DDL Task不一定能够在所有的Clickhouse Node上全部成功,部分成功是常态;过去Clickhouse 在这种情况下会出现各个Clickhouse Node的状态不一致,我们引入了回滚机制,如果任意一个任务失败,整个Job就会失败,保持各个Clickhouse Node的状态一致.

垃圾清理机制:Clickhouse Node自身会定时的跟Master Node做状态同步,清理本地的垃圾Table或者数据目录。

 

资料领取直通车:Golang云原生最新资料+视频学习路线

Go语言学习地址:Golang DevOps项目实战

2.3 基于存算分离架构的多副本

多副本技术是分布式系统底层存储的核心的机制,任何一个分布式系统都有大量的代码在处理多副本, Clickhouse 面临的很多问题也是由于多副本引起的。当前Clickhouse的多副本机制如下所示:

(当前Clickhouse多副本机制)

当前架构有以下缺陷:

  • 依赖Zookeeper存储Log、Parts信息,在Local Table数量变多、数据量变多、导入频繁后会成为瓶颈,所以可以看到业界有很多改进都在设法降低Zookeeper中存储的元数据量,或者把不同的表放到不同的Zookeeper集群中;
  • 成本高,云上一般都是使用云磁盘,Clickhouse继续做副本结果导致磁盘存储成本高。比如云磁盘自身3副本,Clickhouse的ReplicationMergeTree 2副本,结果最后数据实际是6副本;此外云上很多客户都是中小客户,很多客户的Clickhouse集群的规模小于10,而Zookeeper本身就需要3个节点,带来了一些附加成本;

使用了共享存储之后,我们支持多读多写的模型,多副本管理的架构如下:

 

方案概要:

  • 数据放到共享存储上,一个批量数据写入会产生一个Part;
  • 通过一个Commit Log记录Part的变更,比如Add Part, Remove Part等信息;
  • 所有的Clickhouse Replica读取Commit Log,根据Commit Log中记录的操作,更新本机内存中的Part列表信息,由于Part数据文件在共享存储上,所以不需要拉数据,直接加载就好。

为了能够让所有的副本都可以提供读写服务,基于Commit Log,我们增加了冲突处理机制,思路如下图所示:

(冲突处理机制思路图)

  • 每个part 有一个唯一的UUID,不再使用part name做文件夹名字,而是使用uuid做文件夹名字,因为使用part name作为文件夹名字会带来名字冲突;
  • Commit Log提供写的冲突、回放机制,上层的Clickhouse进程写入Commit Log遇到冲突时,需要Replay CommitLog 然后重新提交Part信息;
  • Commit Log定期做Snapshot,一方面可以做Commit Log的回收,另外一方面也降低每次重启获取最新元数据的时间。

在存算分离架构下,成本相对于原来有大幅度降低:

  • 多个副本之间共享物理存储,相比过去2副本的情况下,存储成本至少降低一半;
  • 每个集群不需要单独部署Zookeeper,可以省去至少3个节点的资源成本;
  • 多读多写的模型,不存在只读备份节点的资源浪费,可提升资源利用率。

同时,多读多写的模型消除了传统主从副本在故障时的复杂切换逻辑,任意节点挂掉,其他副本都可以轻松接管读写,大幅提高系统可用性。

2.4 高可用无状态的数据服务层

Clickhouse社区目前有基于S3的多副本机制,也能够降低多副本带来的存储成本,但是这种方案目前有以下问题:

  • 共享存储需要依赖Zookeeper来协调,比如记录part的引用计数信息,Zookeeper记录的元数据更多了,每个Clickhouse Node与Zookeeper的交互更多了;
  • 在每个Clickhouse Node上仍需要记录一部分信息:
    • 在Metadata目录下仍保留存储表的元数据;
    • 每个数据文件会对应一个本地小文件,存储了数据文件到S3对象的映射关系;
  • 由于本地元数据的存在,所以Clickhouse Node本身不是无状态的,有以下问题:
    • 在节点宕机磁盘损坏时,这部分元数据就丢了,存储到S3上的数据在缺少元数据的情况下,也是不可用的;
    • 副本迁移的时候,虽然不需要拷贝数据了,但是元数据仍然需要同步,仍然使用了当前的多副本机制,在线上运维过程中发现这块代码的问题还是比较多的;

我们的改造思路是把所有的元数据都与本地存储剥离,使得基于Clickhouse的计算层彻底无状态:

  • Master Node保存了表的Schema元数据信息,每个Clickhouse Node本地的元数据只是一个缓存,一旦丢失,立即从Master同步;
  • 重新实现的基于共享存储的Part管理机制,将Part所有的信息都放到共享存储上,本地不再保存;

在存算分离模式下,多副本的目标已经从保证数据的可靠性转变为保证服务的可用性,通过把每个副本本地的状态消除,可以任意增加副本的数目,提升服务的可用性而不需要付出存储成本;另外还可以做到查询级别的调度,根据每个节点的健康状况和负载情况,把查询调度到合适的节点上来执行,而不需要复杂的多副本同步,服务整体的可用性大幅度提升。

2.5 秒级的弹性伸缩能力

在数据服务层完全无状态后,除了高可用之外,带来的一个巨大的优势是弹性,节点的加入和退出不需要复杂的数据同步机制,可以在秒级完成集群的伸缩:

  • 新节点加入时只需要从Master上获取表的Schema元数据,再从共享存储上获得Part元数据信息,就可以直接提供服务了,可以在秒级完成;
  • 旧节点删除,不需要等待数据迁移完成,可以直接下线,可以在秒级完成;

集群能够在秒级完成扩缩容有以下优势:

  • 低成本,分析型业务对系统资源的需求有明显的波峰、波谷的特点,例如,白天的资源消耗比晚上多,工作日比周末多,通过制定合适的集群伸缩计划可以显著降低对资源的需求;
  • 高性能,过去数据和节点是绑定的关系,一直遵循着计算跟着数据走的原则,而数据是在节点本地,所以计算是很难扩展的,当计算层无状态后,在一些场景下(比如大查询)可以直接扩充更多的计算节点,快速完成查询任务。

2.6 持续兼容开源生态

在改造Clickhouse的过程中,我们跟很多不同的团队做了一些交流,发现一个非常大的问题就是大家对Clickhouse做了大量的修改,然而由于各种原因这部分代码没能合并到社区,最终跟社区分叉了,所以出现了XXX版本Clickhouse;这一幕在过去10年的大数据历程中反复出现,我们都见过XXX公司内部版本的HDFS、HBase、Kafka等,而这些所谓的自研版本几乎都失败了。Clickhouse的功能迭代速度是很快的,比如最近社区推出的LLVM表达式优化、异步的Pipeline执行、zOrder等,都是非常重要的feature,对性能提升十分明显,一旦分叉这些功能就都用不上了,所以我们坚信兼容Clickhouse社区虽然会慢点,我们的设计会复杂点,但是会走的更远。

对于一个云服务而言,我们面对着各种各样的用户,很多用户的诉求是我们提供的Clickhouse能够跟随社区的版本升级。所以我们在项目伊始就定下了一个原则----尽量少的侵入Clickhouse,能够跟进开源社区Clickhouse进行持续升级。

(模块结构图)

模块结构如上图所示,在我们的架构中,Clickhouse实际是一个单机的库,所以虽然我们实现了复杂的控制流和存算分离的功能,但是通过精巧的设计,基本上对Clickhouse没有侵入,改动了极少的代码,这使得后续的版本升级更加方便,能够随时合并Clickhouse社区的最新功能。

通过把Clickhouse进行云原生改造,与现状相比有以下优势:

• 简单、易维护:通过简单易用的集群管理、统一共享的分布式任务调度服务,可以大幅降低运维人员的运营压力,降低用户的使用门槛,运维同学可以一键完成集群伸缩、用户可以统一入口简单完成DDL操作等,整个系统更为简单易维护。

• 高可用、可扩展:整个系统的架构设计有充分的可用性考虑,各组件的容灾能力都颇具创新,后续也会继续增强;此外,消除了Zookeeper这个明显的中心化瓶颈后,系统支持的数据量可无限扩展,表数量仅受限于Master Node内存,在压力测试中,能够支持500万以上的Table,可覆盖绝大部分需求。

• 低成本:通过存算分离技术,存储成本至少降低了50%,消除了副本间的冗余写入开销,也去除了Zookeeper带来的附加成本;多读多写的模型保障了各个副本之间实时强一致,用户不需要在导入性能和一致性等级之间做权衡;资源调度方面,用户也可以根据集群的负载情况,秒级的弹性伸缩集群资源,可用于降低成本,也可用于提升一部分场景下的查询性能。

• 兼容开源,复用超高性能:持续完整的兼容性,对外的协议、语法、数据存储格式都完全兼容Clickhouse现有的版本,用户可以很方便的迁移,也可以充分享用ClickHouse的超高性能。

未来工作

  • MPP架构的查询引擎:这部分已经在紧张的设计开发中,我们以Clickhouse当前的向量化算子为基础,增加查询优化器、分布式join、聚合计算等功能,预期在今年晚些时候上线;除了支持Clickhouse之外,高性能的MPP执行框架还将支持对HDFS、对象存储、MySQL、Hive、Elasticsearch等多个数据源的查询,提升多源数据查询的速度。
  • 消除Shard,实现简单完全的分布式化:Shard是分布式领域的一个重要概念,主要是做数据在各个节点上的分布打散,然而这是分布式系统自身内部的逻辑,用户一般无需关注Shard,用户希望看到的是一个高度抽象、类单机的分布式系统。此外,当前数据副本和机器绑死的Shard方式,不利于充分发挥分布式系统在伸缩、故障恢复时的潜能,比如扩容时要求成倍进行、故障恢复时压力全部集中在少量节点上。所以我们也在努力改变这种使用方式方式,降低用户的使用负担或顾虑。

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

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

相关文章

T292114 [传智杯 #5 练习赛] 清洁工

题目描述 有一个 n\times nnn 的地块,一个连续 ii 分钟没人经过的地面在第 ii 分钟会落上 ii 个单位的灰,有人经过时不会落灰但灰也不会清零,在人走后第一分钟又会落上一个单位的灰,以此类推。你在这个 n\times nnn 的范围内移动…

深度可分离卷积神经网络与卷积神经网络

在学习语义分割过程中,接触到了深度可分离卷积神经网络,其是对卷积神经网络在运算速度上的改进,具体差别如下: 一些轻量级的网络,如mobilenet中,会有深度可分离卷积depthwise separable convolution&#…

Xamarin.Andorid实现界面弹框

目录1、使用系统自带的样式1.1 具体实现1.2 效果2、自定义样式的实现2.1 预期效果2.2 具体实现2.3 相关知识3 代码下载4、参考在App的实际使用中,一定会出现弹框选择的情况。如图所示: 因此非常有必须学会及使用弹框的功能,因此本次学习Xama…

miui刷机完整教程

风险提示:刷机有可能损害手机,本作者不承担因为使用本方法刷机引起的任何问题,请谨慎刷机。 1.选择[刷机包](https://web.vip.miui.com/page/info/mio/mio/detail?postId37093637&app_versiondev.20051) 2.解BL锁 浏览器打开http://www…

SSM整合(二)

SSM框架整合之mybatis查询的两个例子 1 准备工作 1.1 创建查询工作所需要的实体类Emp package com.entity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import tk.mybatis.mapper.annotation.KeySql; import javax.persisten…

代码随想录算法训练营第四天|24、19、面试题 02.07、142、92(寄了)

Leecode24. 两两交换链表中的节点 链接:https://leetcode.cn/problems/swap-nodes-in-pairs/ 其实这道题的思路和203比较相似,因为若是不设置虚拟头结点的话,处理头结点的方式和处理非头结点的方式会不一样,所以还是设置虚拟头结…

LeetCode力扣刷题——指针三剑客之三:图

图 一、数据结构介绍 作为指针三剑客之三,图是树的升级版。图通常分为有向(directed)或无向(undirected),有 循环(cyclic)或无循环(acyclic),所有…

Golang入门笔记(15)—— 数组和切片

编程的世界中,或许是因为一次一次的定义变量,维护管理起来都太费劲了,所以推出了数组,将数据用数组的形式管理起来。 Go的数组和Java的实现机制是不同的,Go语言的数组是作为基本数据类型存在的。所以数组是开辟在栈帧中…

Golang Web开发一键生成各层级模板代码

文章目录go_project_quickstart快速开始要求安装项目实现架构设计代码逻辑统一的调用逻辑代码复用每次写web项目,模板都是统一的,每次都要写大量冗余的代码会很烦,在Java中有很多逆向生成的工具,而Go语言我找了很久,也…

手把手搭建springboot项目,并测试springDataJPA

这篇文章记录了搭建springboot项目并测试springDataJPA的过程,接下来一起看看吧。 1.访问阿里云java脚手架网站 点击访问 2.按照下图勾选并获取代码 3.将获取到的代码解压到idea工作空间。 下图的JPAdemo就是我的项目 4.打开idea,导入项目 5.在pom…

【Redis】| 01 | Redis 可视化工具RedisInsight

目录1 RedisInsight 简介2 RedisInsight 安装2.1 安装2.1.1 软件安装2.1.1.1 下载 RedisInsight 软件包2.1.1.2 安装命令2.1.1.3 配置环境变量2.1.1.4 启动服务2.1.2 Kubernetes 安装2.1.2.1 创建 RedisInsight 的 yaml 文件2.1.2.2 启动3.RedisInsight 使用3.1 安装redis3.1.1…

Jacobi迭代的MPI进阶——计算通信重叠和虚拟进程的使用

1:条形分割的几种策略 这里先放Makefile和run.slurm Makefile CC = mpicc FLAGS = -O3 -Wall OBJ = *.o EXE = hang hangstartall Ihang xunihang qipan srqipan Isrqipan qipanendall: ${EXE}hang: hang.c $(CC) -o $@ $^ $(FLAGS)

信息安全结业复习题(选择 + 填空 + 简答 + 计算 + 设计 )含历年考题

这里写目录标题客观题知识点简答题计算题设计题客观题知识点 考过填空 密码学是保障信息安全的核心、信息安全是密码学研究和发展的目标 保证数字信息机密性的最有效方法是使用密码算法对其进行加密(对称和非对称) 保证信息完整性的有效方法是利用hash…

SQL Server全套教程(基于SQL语句----续更中)

SQL Server全套教程全程干货1. 数据库的基础操作1.1.0 创建数据库1.1.1 查看及修改数据库1.1.3 分离、附加和删除数据库2.数据库表的相关操作2.1.0 常用数据类型2.1.1 表结构的创建2.1.2 表结构的查看及修改1. 数据库的基础操作 1.1.0 创建数据库 数据库创建语法 -- 创建数据…

MicroPython-On-ESP8266——8x8LED点阵模块(2)使用74HC595驱动

MicroPython-On-ESP8266——8x8LED点阵模块(2)使用74HC595驱动 1. 使用74HC595驱动的原理 1.1. 基础回顾 上篇我们学习了8x8LED点阵屏的电路基础知识和驱动的原理,见 8x8LED点阵模块(1)驱动原理 里面也提到了&…

基于ssm+Javaweb的OA小型企业资源管理系统-计算机毕业设计

小型OA企业资源管理系统基于SSM(SpringSpringMVCMyBatis)框架,适用于毕业设计,采用javaweb,基于B/S模式,Myeclipse或者eclipse idea为开发工具,mysql数据库,主要包括登录模块、人事管理模块、公告管理模块、产品管理模块、和退出模块等多个模块。 本系统主要包含了…

MySQL高级篇知识点——主从复制

目录1.主从复制概述1.1.如何提升数据库并发能力1.2.主从复制的作用2.主从复制的原理2.1.原理剖析2.2.复制的基本原则3.一主一从架构搭建3.1.准备工作3.2.主机配置文件3.3.从机配置文件3.4.主机:建立账户并授权3.5.从机:配置需要复制的主机3.6.测试3.7.停…

tensorflow2.x --------------------DenseNet-----------------------------

用tensorflow2.4实现了DenseNet-121,训练基于ImageNet图像数据集,图片输入大小为 224x224 。网络结构采用包含4个DenseBlock的DenseNet-BC,每个DenseNet-BC由若干个 BNReLU1x1 ConvBNReLU3x3 Conv(Dense_layer)且每个DenseBlock的特征图大小分…

听我劝,自学游戏建模真的很难

我的个人建议一直是偏向不要自学建模的(特别是零基础🤣),学习3D游戏建模除了单纯需要为爱发电还需要能够自行✅掌握软件操作技巧和✅纠错改正,后者都是自学非常❌难做到的😭 除此之外,还有以下…

详解Unity中的Nav Mesh新特性|导航寻路系统 (二)

前言 第一篇我们简要概述了新版NavMesh的一些新增功能,以及旧版的对比。本篇我们来讲一讲NavMeshSurface、NavMeshLink这两个组件的参数以及如何应用,接下来就进入正题吧。 本系列提要 Unity新版NavMesh专题博客共分成三篇来讲解: 【本篇为…