mapreduce原理_Hbase Bulkload 原理面试必备

当需要大批量的向Hbase导入数据时,我们可以使用Hbase Bulkload的方式,这种方式是先生成Hbase的底层存储文件 HFile,然后直接将这些 HFile 移动到Hbase的存储目录下。它相比调用Hbase 的 put 接口添加数据,处理效率更快并且对Hbase 运行影响更小。

下面假设我们有一个 CSV 文件,是存储用户购买记录的。它一共有三列, order_id,consumer,product。我们需要将这个文件导入到Hbase里,其中 order_id 作为Hbase 的 row key。

12345
bin/hbase org.apache.hadoop.hbase.mapreduce.ImportTsv -Dimporttsv.separator=$'\x01'-Dimporttsv.columns=HBASE_ROW_KEY,cf:consumer,cf:product -Dimporttsv.bulk.output= bin/hbase org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles 

可以看到批量导入只需要上述两部, 生成 HFile 文件 和 加载 HFile 文件。下面我们来深入了解其原理

底层实现原理

生成 HFile 是调用了 MapReduce 来实现的。它有两种实现方式,虽然最后生成的 HFile 是一样的,但中间过程却是不一样。现在我们先回顾下 MapReduce 的编程模型,主要分为下列组件:

  • InputFormat:负责读取数据源,并且将数据源切割成多个分片,分片的数目等于Map的数目

  • Mapper:负责接收分片,生成中间结果,K 为数据的 key 值类型,V为数据的 value 值类型

  • Reducer:Mapper的数据会按照 key 值分组,Reducer接收的数据格式>

  • OutputFormat:负责将Reducer生成的数据持久化,比如存储到 hdfs。

MapReduce 实现 一

MapReducer 程序中各个组件的实现类,如下所示:

  • InputFormat 类:TextInputFormat,数据输出格式 LongWritable,Text(数据所在行号,行数据)

  • Mapper 类:TsvImporterTextMapper,数据输出格式 ImmutableBytesWritable, Text(row key,行数据)

  • Reduce 类:TextSortReducer,数据输出格式 ImmutableBytesWritable, KeyValue (row key,单列数据)

  • OutputFormat 类:HFileOutputFormat2,负责将结果持久化 HFile

执行过程如下:

  1. TextInputFormat 会读取数据源文件,按照文件在 hdfs 的 Block 切割,每个Block对应着一个切片

  2. Mapper 会解析每行数据,然后从中解析出 row key,生成(row key, 行数据)

  3. Reducer 会解析行数据,为每列生成 KeyValue。这里简单说下 KeyValue,它是 Hbase 存储每列数据的格式, 详细原理后面会介绍到。如果一个 row key 对应的列过多,它会将列分批处理。处理完一批数据之后,会写入(null,null)这一条特殊的数据,表示 HFileOutputFormat2 在持久化的过程中,需要新创建一个 HFile。

这里简单的说下 TextSortReducer,它的原理与下面的实现方式二,使用到的 PutSortReducer 相同,只不过从 Map 端接收到的数据为原始的行数据。如果 row key 对应的数据过多时,它也会使用 TreeSet 来去重,TreeSet 保存的数据最大字节数,不能超过1GB。如果超过了,那么就会分批输。

MapReduce 实现 二

MapReducer 程序中各个组件的实现类,如下所示:

  • InputFormat 类:TextInputFormat,数据输出格式 LongWritable,Text(数据所在行号,数据)

  • Mapper 类:TsvImporterMapper,数据输出格式 ImmutableBytesWritable,Put (row key,Put)

  • Combiner 类:PutCombiner

  • Reducer 类:PutSortReducer,数据输出格式 ImmutableBytesWritable, KeyValue(row key,单列数据)

  • OutputFormat 类:HFileOutputFormat2,负责将结果持久化 HFile

这里使用了 Combiner,它的作用是在 Map 端进行一次初始的 reduce 操作,起到聚合的作用,这样就减少了 Reduce 端与 Map 端的数据传输,提高了运行效率。

执行过程如下:

  1. TextInputFormat 会读取数据源文件,原理同实现 一

  2. Mapper 会解析每行数据,然后从中解析出 row key,并且生成 Put 实例。生成(row key, Put)

  3. Combiner 会按照 row key 将多个 Put 进行合并,它也是分批合并的。

  4. Reducer 会遍历 Put 实例,为每列生成 KeyValue 并且去重。

这里讲下PutSortReducer的具体实现,下面的代码经过简化,去掉了KeyValue中关于Tag的处理:

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
public class PutSortReducer extends    Reducer<ImmutableBytesWritable, Put, ImmutableBytesWritable, KeyValue> {  // the cell creator  private CellCreator kvCreator;    @Override  protected void reduce(      ImmutableBytesWritable row,      java.lang.Iterable puts,      Reducer              ImmutableBytesWritable, KeyValue>.Context context)      throws java.io.IOException, InterruptedException  {    // 这里指定了一个阈值,默认为10GB过大。如果puts中不重复的数据过大,就会按照这个阈值分批处理    long threshold = context.getConfiguration().getLong(        "putsortreducer.row.threshold", 1L * (1<<30));    Iterator iter = puts.iterator();    // 开始遍历 puts列表    while (iter.hasNext()) {      // 这个TreeSet就是用来去重的,比如向同个qualifier添加值      TreeSet map = new TreeSet<>(CellComparator.getInstance());      // 记录map里保存的数据长度      long curSize = 0;      // 遍历 puts列表,直到不重复的数据不超过阈值      while (iter.hasNext() && curSize < threshold) {        // 从列表中获取值        Put p = iter.next();        // 遍历这个Put的所有列值,一个Put包含了多列,这些列由Cell表示        for (List cells: p.getFamilyCellMap().values()) {          for (Cell cell: cells) {            KeyValue kv = null;            kv = KeyValueUtil.ensureKeyValue(cell);            }            if (map.add(kv)) {              // 如果这列值没有重复,那么添加到TreeSet中,并且更新curSize的值              curSize += kv.heapSize();            }          }        }      }      // 将map里的数据,调用context.write方法输出      int index = 0;      for (KeyValue kv : map) {        context.write(row, kv);        if (++index % 100 == 0)          context.setStatus("Wrote " + index);      }      // 如果还有,那么说明此行数据过大,那么就会输出一条特殊的记录(null, null)      if (iter.hasNext()) {        // force flush because we cannot guarantee intra-row sorted order        context.write(null, null);      }    }  }}

从上面的代码可以看到,PutSortReducer会使用到TreeSet去重,TreeSet会保存数据,默认不超过 1GB。如果当Reducer的内存设置过小时,并且数据过大时,是有可能会造成内存溢出。如果遇到这种情况,可以通过减少阈值或者增大Reducer的内存。

两种实现方式比较

第一种方式实现简单,它从Map 端传递到 Reduce 端的中间结果的数据格式很紧凑,如果是数据源重复的数据不多,建议使用这种。

第二种方式实现相对复杂,它从Map 端传递到 Reduce 端的中间结果的数据格式,使用 Put 来表示,它的数据存储比原始的数据要大。但是它使用了 Combiner 来初步聚合,减小了 Map 端传递到 Reduce 端的数据大小。如果是数据源重复比较多,建议采用第二种方式。

Hbase 默认采用第二种方式,如果用户想使用第一种方式,需要在运行命令时,指定 importtsv.mapper.class 的值为 org.apache.hadoop.hbase.mapreduce.TsvImporterTextMapper。

数据源解析

Mapper 接收到数据后,需要解析每行数据,从中读取各列的值。它会按照分割符来切割数据,然后根据指定的列格式,生成每列的数据。客户在使用命令时,通过 importtsv.separator 参数指定分隔符,通过 importtsv.columns 参数指定列格式。在客户端指定的列名中, 有些会有着特殊含义,比如 HBASE_ROW_KEY 代表着该列是作为 row key,HBASE_TS_KEY 代表着该列作为数据的 timestamp,HBASE_ATTRIBUTES_KEY 代表着该列是属性列等。

TsvParser 类负责解析数据,它定义在 ImportTsv 类里。这里需要注意下,它不支持负责的 CSV 格式,只是简单的根据分隔符作为列的划分,根据换行符作为每条数据的划分。

它的原理比较简单,这里不再详细介绍。

Reducer的数目选择

我们知道MapReduce程序的一般瓶颈在于 reduce 阶段,如果我们能够适当增加 reduce 的数目,一般能够提高运行效率(如果数据倾斜不严重)。我们还知道 Hbase 支持超大数据量的表,它会将表的数据自动切割,分布在不同的服务上。这些数据切片在 Hbase 里,称为Region, 每个Region只负责一段 row key 范围的数据。

Hbase 在批量导入的时候,会去获取表的 Region 分布情况,然后将 Reducer 的数目 设置为 Region 数目。如果在导入数据之前还没有创建表,Hbase会自动创建,但是创建的表的region数只有一个。所以在生成HFile之前,我们可以自行创建表,并指定 Reigion 的分布情况,那么就能提高 Reducer 的数目。

Reducer 的数目决定,是在 HFileOutputFormat2 的 configureIncrementalLoad 方法里。它会读取表的 region 分布情况,然后调用 setNumReduceTasks 方法设置 reduce 数目。下面的代码经过简化:

12345678910111213141516171819202122232425
public class HFileOutputFormat2    extends FileOutputFormat<ImmutableBytesWritable, Cell> {        public static void configureIncrementalLoad(Job job, TableDescriptor tableDescriptor,      RegionLocator regionLocator) throws IOException {    	ArrayList singleTableInfo = new ArrayList<>();    	singleTableInfo.add(new TableInfo(tableDescriptor, regionLocator));    	configureIncrementalLoad(job, singleTableInfo, HFileOutputFormat2.class);  	}      	static void configureIncrementalLoad(Job job, List multiTableInfo,      	Class extends OutputFormat, ?>> cls) throws IOException {        // 这里虽然支持多表,但是批量导入时只会使用单表        List regionLocators = new ArrayList<>( multiTableInfo.size());           	for( TableInfo tableInfo : multiTableInfo )      	{            // 获取region分布情况      		regionLocators.add(tableInfo.getRegionLocator());     		......      	}        // 获取region的row key起始大小      	List startKeys = getRegionStartKeys(regionLocators, writeMultipleTables);        // 设置reduce的数目      	job.setNumReduceTasks(startKeys.size());    }  }

Hbase 数据存储格式

Hbase的每列数据都是单独存储的,都是以 KeyValue 的形式。KeyValue 的数据格式如下图所示:

123
----------------------------------------------- keylength | valuelength | key | value | Tags-----------------------------------------------

其中 key 的格式如下:

123
---------------------------------------------------------------------------------------------- rowlength | row | columnfamilylength | columnfamily | columnqualifier | timestamp | keytype ----------------------------------------------------------------------------------------------

Tags的格式如下:

123
------------------------- tagslength | tagsbytes -------------------------

tagsbytes 可以包含多个 tag,每个 tag 的格式如下:

123
---------------------------------- taglength | tagtype | tagbytes----------------------------------

Reducer 会使用 CellCreator 类,负责生成 KeyValue。CellCreator 的原理很简单,这里不再详细介绍。

生成 HFile

HFileOutputFormat2 负责将Reduce的结果,持久化成 HFile 文件。持久化目录的格式如下:

1234567
.|---- column_family_1|    |---- uuid_1|    `---- uuid_2|---- column_family_2|    |---- uuid3|    `---- uuid4

每个 column family 对应一个目录,这个目录会有多个 HFile 文件。

HFileOutputFormat2 会创建 RecordWriter 实例,所有数据的写入都是通过 RecordWriter。

12345678910111213141516171819
public class HFileOutputFormat2    extends FileOutputFormat<ImmutableBytesWritable, Cell> {      @Override  public RecordWritergetRecordWriter(      final TaskAttemptContext context) throws IOException, InterruptedException {    // 调用createRecordWriter方法创建    return createRecordWriter(context, this.getOutputCommitter(context));  }      static  RecordWriter      createRecordWriter(final TaskAttemptContext context, final OutputCommitter committer)          throws IOException {      // 实例化一个匿名类      return new RecordWriter() {          ......      }  }}

可以看到 createRecordWriter 方法,返回了一个匿名类。继续看看这个匿名类的定义:

123456789101112
// 封装了StoreFileWriter,记录了写入的数据长度static class WriterLength {    long written = 0;    StoreFileWriter writer = null;}class RecordWriter<ImmutableBytesWritable, V>() {    // key值为表名和column family组成的字节,value为对应的writer    private final Map<byte[], WriterLength> writers = new TreeMap<>(Bytes.BYTES_COMPARATOR);    // 是否需要创建新的HFile    private boolean rollRequested = false;}

从上面 WriterLength 类的定义,我们可以知道 RecordWriter的底层原理是调用了StoreFileWriter的接口。对于StoreFile,我们回忆下Hbase的写操作,它接收客户端的写请求,首先写入到内存中MemoryStore,然后刷新到磁盘生成StoreFile。如果该表有两个column family,就会有两个MemoryStore和两个StoreFile,对应于不同的column family。所以 RecordWriter 类有个哈希表,记录着每个 column family 的 StoreFileWriter。(这里说的 StoreFile 也就是 HFile)

因为 HFile 支持不同的压缩算法,不同的块大小,RecordWriter 会根据配置,获取HFile的格式,然后创建对应的 StoreFileWriter。下面创建 StoreFileWriter 时只指定了文件目录,StoreFileWriter会在这个目录下,使用 uuid 生成一个唯一的文件名。

1234567891011121314151617181920212223242526272829303132333435363738
class RecordWriter<ImmutableBytesWritable, V>() {    // favoredNodes 表示创建HFile文件,希望尽可能在这些服务器节点上    private WriterLength getNewWriter(byte[] tableName, byte[] family, Configuration conf, InetSocketAddress[] favoredNodes) throws IOException {        // 根据表名和column family生成唯一字节        byte[] tableAndFamily = getTableNameSuffixedWithFamily(tableName, family);        Path familydir = new Path(outputDir, Bytes.toString(family));        WriterLength wl = new WriterLength();        // 获取HFile的压缩算法        Algorithm compression = compressionMap.get(tableAndFamily);        // 获取bloom过滤器信息        BloomType bloomType = bloomTypeMap.get(tableAndFamily);        // 获取HFile其他的配置        .....        // 生成HFile的配置信息        HFileContextBuilder contextBuilder = new HFileContextBuilder()                                    .withCompression(compression)                                    .withChecksumType(HStore.getChecksumType(conf))                                    .withBytesPerCheckSum(HStore.getBytesPerChecksum(conf))                                    .withBlockSize(blockSize);        HFileContext hFileContext = contextBuilder.build();        // 实例化 StoreFileWriter        f (null == favoredNodes) {          wl.writer =              new StoreFileWriter.Builder(conf, new CacheConfig(tempConf), fs)                  .withOutputDir(familydir).withBloomType(bloomType)              .withComparator(CellComparator.getInstance()).withFileContext(hFileContext).build();        } else {          wl.writer =              new StoreFileWriter.Builder(conf, new CacheConfig(tempConf), new HFileSystem(fs))                  .withOutputDir(familydir).withBloomType(bloomType)                  .withComparator(CellComparator.getInstance()).withFileContext(hFileContext)                  .withFavoredNodes(favoredNodes).build();        }        // 添加到 writers集合中        this.writers.put(tableAndFamily, wl);        return wl;    }}

继续看看 RecordWriter 的写操作:

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
class RecordWriter<ImmutableBytesWritable, V>() {    @Override    public void write(ImmutableBytesWritable row, V cell)        Cell kv = cell;         // 收到空数据,表示需要立即刷新到磁盘,并且创建新的HFile    	if (row == null && kv == null) {            // 刷新到磁盘            rollWriters(null);            return;        }        // 根据table和column family生成唯一值    	byte[] tableAndFamily = getTableNameSuffixedWithFamily(tableNameBytes, family);        // 获取对应的writer    	WriterLength wl = this.writers.get(tableAndFamily);    	if (wl == null) {            // 如果为空,那么先创建对应的文件目录            Path writerPath = null;            writerPath = new Path(outputDir, Bytes.toString(family));            fs.mkdirs(writerPath);        }        // 检测当前HFile的大小是否超过了最大值,默认为10GB    	if (wl != null && wl.written + length >= maxsize) {            this.rollRequested = true;        }        // 如果当前HFile过大,那么需要将它刷新到磁盘    	if (rollRequested && Bytes.compareTo(this.previousRow, rowKey) != 0) {            rollWriters(wl);        }        // 创建writer    	if (wl == null || wl.writer == null) {            if (conf.getBoolean(LOCALITY_SENSITIVE_CONF_KEY, DEFAULT_LOCALITY_SENSITIVE)) {                // 如果开启了位置感知,那么就会去获取row所在的region的地址                HRegionLocation loc = null;                loc = locator.getRegionLocation(rowKey);                InetSocketAddress initialIsa = new InetSocketAddress(loc.getHostname(), loc.getPort());                // 创建writer,指定了偏向节点                wl = getNewWriter(tableNameBytes, family, conf, new InetSocketAddress[] { initialIsa})            } else {                // 创建writer                wl = getNewWriter(tableNameBytes, family, conf, null);            }        }    	wl.writer.append(kv);    	wl.written += length;    	this.previousRow = rowKey;	}	private void rollWriters(WriterLength writerLength) throws IOException {        if (writerLength != null) {            // 关闭当前writer            closeWriter(writerLength);        } else {            // 关闭所有family对应的writer            for (WriterLength wl : this.writers.values()) {                closeWriter(wl);            }        }        this.rollRequested = false;    }	private void closeWriter(WriterLength wl) throws IOException {        if (wl.writer != null) {            close(wl.writer);        }        wl.writer = null;        wl.written = 0;    }}

RecordWriter在写入数据时,如果遇到一条 row key 和 value 都为 null 的数据时,这条数据有着特殊的含义,表示writer应该立即 flush。在每次创建RecordWriter时,它会根据此时row key 的值,找到所属 Region 的服务器地址,然后尽量在这台服务器上,创建新的HFile文件。

加载 HFile

上面生成完 HFile 之后,我们还需要调用第二条命令完成加载 HFile 过程。这个过程分为两步,切割数据量大的 HFile 文件和发送加载请求让服务器完成。

切割 HFile

首先它会遍历目录下的每个 HFile ,

  1. 首先检查 HFile 里面数据的 family 在 Hbase 表里是否存在。

  2. 获取HFile 数据的起始 row key,找到 Hbase 里对应的 Region,然后比较两者之间的 row key 范围

  3. 如果 HFile 的 row key 范围比 Region 大,也就是 HFile 的结束 row key 比这个 Region 的 结束 row Key 大,那么需要将这个 HFile 切割成两份,切割值为 Region 的结束 row key。

  4. 继续从上一部切割生成的两份HFile中,选择第二份 HFile(它的row key 大于 Regioin 的结束 row key),将它继续按照第二步切割,直到所有HFile的 row key范围都能在一个Region里。

在割切HFile的过程中,还会检查 column family 对应的 HFile数目。如果一个 column family 对应的 HFile 数目过多,默认数目为32,程序就会报错。但是这个值通过指定 hbase.mapreduce.bulkload.max.hfiles.perRegion.perFamily,来设置更大的值。

发送加载请求

当完成了HFile的切割后,最后的导入动作是发送 BulkLoadHFileRequest 请求给 Hbase 服务端。Hbase 服务端会处理该请求,完成HFile加载。

其他

至于我研究 Hbase Bulkload 的原因,是在使用过程中发生了 Out Of Memory 的错误。虽然经过排查,发现和 Hbase Bulkload 的原理没什么关系,不过在此也顺便提一下,希望能帮到遇到类似情况的人。首先说下我使用的Hadoop 版本是 CDH 5.12.2。

经过排查,发现是因为 Hbase Bulkload 底层用的 MapReduce 模式为本地模式,而不是集群 Yarn 的方式。我们知道 MapReduce 程序选择哪一种方式,可以通过 mapreduce.framework.name 配置项指定。虽然在 CDH 的 Yarn 配置页面里,设置了该配置为 yarn,但是 Hbase Bulkload 仍然使用本地模式。后来发现 Yarn 组件下有个 Gateway 的角色实例,这是个特殊的角色,它负责 Yarn 客户端的配置部署。而恰好这台主机没有安装,所以在使用 Hbase Bulkload 时,没有读取到 Yarn 的配置。解决方法是在 CDH 界面添加 Gateway 实例就好了。

d0c9e38d0190e1174be26c888511129e.png

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

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

相关文章

StarLake:汇量科技云原生数据湖的探索和实践

简介&#xff1a; 快速了解汇量科技在云原生数据湖领域的探索和实践&#xff0c;详解 StarLake 的架构及业务应用案例。 作者&#xff1a;陈绪&#xff08;汇量科技资深算法架构师&#xff0c;EnginePlus 2.0 产品负责人&#xff09; 内容框架&#xff1a; 互联网业务视角看湖…

sql语句在navicat中可以查询到所有数据但是在idea程序中不行_数据迁移测试实施方案...

点击关注&#xff0c;我们共同每天进步一点点&#xff01;最近经历了一场大型的数据迁移测试&#xff0c;因为以前对数据迁移测试研究甚少&#xff0c;所以对测试实施方案的制定非常的棘手&#xff0c;在网上也查询了很多&#xff0c;发现相关资料很少&#xff0c;并且大部分都…

报告:69% 的企业表示云技术有助于他们的疫情恢复

根据 DigitalOcean 最近的报告&#xff0c;在疫情高峰期间云使用增加的企业中&#xff0c;86%的企业表示云使用量在 2021 年继续增加&#xff0c;这表明数字加速和云采用没有放缓迹象。随着 2022 年的临近&#xff0c;对于各种规模的企业来说&#xff0c;这场疫情仍是头等大事&…

PyFlink 教程(三):PyFlink DataStream API - state timer

简介&#xff1a; 介绍如何在 Python DataStream API 中使用 state & timer 功能。 一、背景 Flink 1.13 已于近期正式发布&#xff0c;超过 200 名贡献者参与了 Flink 1.13 的开发&#xff0c;提交了超过 1000 个 commits&#xff0c;完成了若干重要功能。其中&#xff…

长跑 11 年,腾讯开源的变与不变

作者 | 贾凯强出品 | CSDN云计算&#xff08;ID&#xff1a;CSDNcloud&#xff09;在中国&#xff0c;开源产业的发展就像是一个美丽的童话故事。90年代&#xff0c;开源如一无所有的灰姑娘&#xff0c;仰望着海外梦幻般的舞会&#xff0c;自己却很难融入其中&#xff1b;而世纪…

.net 批量更新_Revit二次开发——读取CAD文字实现更新模型的思路

更新模型与内地BIM项目中 设计院终版图纸一波流翻模的模式不同香港BIM项目的模式是&#xff1a;设计出图—BIM出碰撞报告—设计再改图—BIM再碰撞报告......反反复复....模型频繁更新 是BIM项目服务过程中不可避免的应对方法&#xff1a;1.晚上加班2.周末加班本文中 模型更新的…

php使用七牛直播,七牛上传文件,PHP版本

自从知道七牛以来&#xff0c;就一直在用七牛做图片外链&#xff0c;但是每次需要到七牛官网登录&#xff0c;然后再上传图片。感觉很麻烦&#xff0c;最近想做一个自己的上传到七牛的平台&#xff0c;开始的想法是用C#写一个windows客户端&#xff0c;在用swift写一个mac客户端…

汽车之家:基于 Flink + Iceberg 的湖仓一体架构实践

简介&#xff1a; 由汽车之家实时计算平台负责人邸星星在 4 月 17 日上海站 Meetup 分享的&#xff0c;基于 Flink Iceberg 的湖仓一体架构实践。 内容简要&#xff1a; 一、数据仓库架构升级的背景 二、基于 Iceberg 的湖仓一体架构实践 三、总结与收益 四、后续规划 一、数据…

基于 Scheduled SQL 对 VPC FlowLog 实现细粒度时间窗口分析

简介&#xff1a; 针对VPC FlowLog的五元组和捕获窗口信息&#xff0c;在分析时使用不同时间窗口精度&#xff0c;可能得到不一样的流量特征&#xff0c;本文介绍一种方法将原始采集日志的时间窗口做拆分&#xff0c;之后重新聚合为新的日志做分析&#xff0c;达到更细粒度的分…

实力登场!移动云技术内核2.0 四大全新升级!

“中国数字经济占GDP比重持续增长&#xff0c;5G网络建设已进入规模化部署阶段。随着5G网络的发展&#xff0c;企业的数字化改造需求越来越旺盛。企业日益增长的数字化改造需求对云基础设施提出了新的挑战&#xff1a;需要支持多种类型网络接入、支持公有云、混合云、专属云等多…

obsidian使用分享

ob对比其他软件 上文提到obsidian&#xff0c;这里对obsidian做一个简要的总结 优点&#xff1a;对比notion&#xff0c;语雀这些软件&#xff0c;内容存储在应用商的服务器上。它是存在本地的。 对比思源笔记。说一下思源笔记的不足。思源是块来控制的&#xff0c;回车就是一…

苹果xr如何截屏_苹果手机自带的三种截屏技巧,你知道几个?现在知道还不迟...

今年苹果手机发布的新机自发布以来就受到了热烈的追捧&#xff0c;销量一直都处于只增不减的趋势。苹果手机为何如此之火&#xff1f;除了本身自带的IOS系统之外&#xff0c;手机自带很多小技巧&#xff0c;你知道不&#xff1f;今天就来为大家介绍苹果手机中的三种截屏小技巧&…

Scheduled SQL: SLS 大规模日志上的全局分析与调度

简介&#xff1a; 本文总结了大规模日志全局分析的需求&#xff0c;讨论SLS上现有的典型分析方案&#xff0c;并延伸到 SLS 原生数据处理方案&#xff0c;介绍 Schedueld SQL 功能与最佳实践。 大规模日志全局分析的需求 数据大规模与时效性 基于时间的数据&#xff08;日志…

matlab制作以太网数据接收上位机_3D激光扫描仪设计及数据处理

本文内容转载自《电子技术应用》2019年第10期&#xff0c;版权归《电子技术应用》编辑部所有。段清明&#xff0c;王凡&#xff0c;徐琳琳&#xff0c;全文俊吉林大学仪器科学与电气工程学院摘要&#xff1a;利用2D激光雷达配合云台装置&#xff0c;设计了一种3D激光扫描仪作为…

跨平台(windows+linux)的线程辅助程序,跨平台(Windows+Linux)的Socket通讯程序(二)—结构...

上一篇"跨平台(WindowsLinux)的Socket通讯程序"给出了Socket通讯底层的一些函数的包装方法/类&#xff0c;同时屏蔽了操作系统(Windows/Linux)的不同。上一篇只是对通讯底层方法的封装&#xff0c;并没用涉及应用&#xff0c;这一篇将基于上一篇&#xff0c;并结合&q…

数据的“敏捷制造”,DataWorks一站式数据开发治理范式演进

简介&#xff1a; 企业大数据技术发展至今&#xff0c;历经了两次蜕变。第一次蜕变从最初的“小作坊”解决大数据问题&#xff0c;到后来企业用各类大数据技术搭建起属于自己的“大平台”&#xff0c;通过平台化的能力完成数据生产力的升级。 第二次蜕变让大数据从“大平台”向…

全新的 Fragment 通信方式

作者 | tech-bus.丹卿来源 | 程序员巴士前言就在前段时间&#xff0c;Google 推出了 Fragment Result API 和 Activity Results API&#xff0c;用来取代之前的 Activity 和 Fragment 之间通信方式的不足&#xff0c;大家可以前往看看都有哪些更新&#xff1a;https://medium.c…

数据传输完整性_电缆监测数据传输系统分析与设计

电缆线路是重要的输电方式&#xff0c;对电缆线路进行监测是保证电缆线路正常工作的重要的条件&#xff0c;研究人员利用嵌入式系统设计了电缆监测数据传输系统。该系统以CAN通信和嵌入式以太网络技术为核心&#xff0c;实现了对电缆及其沟道的实时监测、状态显示及预报警功能&…

大型企业多账号管理“安全心法”

简介&#xff1a; 云上多账号环境下的网络统一管理&#xff0c;是大型分支型企业网络安全防护的必经之路。无论是外企入华、国内企业出海&#xff0c;还是本土集团型企业规模化成长&#xff0c;云上统一网络安全管控与整体安全态势感知&#xff0c;都可以拉齐企业账号间安全水位…

苹果将于 2025 年推出的 Apple Car 长什么样?

整理 | 孙胜出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;据国外媒体报道&#xff0c;苹果公司预计将于2025年推出一款全新的自动驾驶汽车&#xff0c;旨在实现真正意义上的无人驾驶。报道称&#xff0c;基于自动驾驶的理念&#xff0c;苹果理想的汽车没有方向盘…