基于 C# 的 ETL 大数据并行编程

作者:James Spinella

译者:精致码农

原文:https://bit.ly/3nGQu4J

并行编程在历史上一直是软件开发中比较小众和复杂的环节,往往不值得头疼。但编写并行化应用只会越来越简单,一个应用同时利用设备 CPU 上的多个内核,来实现效率最大化也是很常见的。

如今,随着数据工程作为一个专业领域的兴起,并行编程比以往任何时候都更受欢迎。Apache Spark 是一个用于Extract(提取), Transform(转换) 和 Load(加载)——ETL 大型数据集的软件库,可能是当今最流行的并行编程的方式。虽然 Apache 的 Spark、Hadoop 和 AirFlow 是对数据工程师来说最常见的技术,但它们的使用要求精通的不是 C#,而是 Python、Scala 或 Java尽管不是很理想)。

对于 ETL 工作来说,“正确”的工具应该是 Spark 或 Hadoop 这样的工具。它们是专门为 ETL 设计的,与 C# 或其他语言相比,需要你编写的代码更少。如果你想在数据工程这个领域深耕,你最好的选择学习一下 Python 和 Spark(以及其他技术)。尽管如此,但有时正确的工具是你已经知道如何使用的工具。事实上,我已经发现 C# 和 .NET 可以胜任并行化 ETL 操作的任务。

微软也一直在开发 .NET for Apache Spark[1],它允许我们即使只会 C# 也能吃上 Spark 这块蛋糕。很大程度上仍在开发中,但目前你已经可以开始尝试了。(译注:想了更多关于 .NET for Apache Spark 的内容,可以阅读我的另一篇译文:使用 .NET 5 体验大数据和机器学习)

多年来,C# 的发展使并行编程变得越来越简单。因为 C# 与以前的版本保持 100% 的向后兼容,所以很难知道在众多并行运行代码的方法中哪种是最好的方法。事实上,在 .NET 中,有好几种方法可以启动多个线程,而真正的问题在于,你希望 .NET 在“背后”为你处理多少和你希望自己手动处理多少。

一般来说,我们确实希望 .NET 尽可能多的为我们处理,特别是在线程管理方面,因为并行运行代码是(编程时)非常复杂的,而且非常容易出现意外的运行时错误。事实上,微软在这里[2]有一篇专门的文档涵盖了并行编程的潜在陷阱。

我建议大家读一读,但就 ETL 和其他“数据处理”任务而言,我们真正需要担心的只有两件事:并行化是否真的会更快,如果是,确保我们的代码是线程安全的。

CPU密集型 vs IO密集型

在确定“并行化”代码是否值得时,重要的是要了解应用程序的哪些部分是 CPU 密集型(CPU-bound)而不是 IO 密集型(IO-bound)的。正如你可能已经猜到的那样,并行化增强了 CPU 密集型代码的性能,它不仅不会对任何 IO 瓶颈(bottlenecks)产生改善,而且可能会加剧应用程序的 IO 瓶颈(相比之下,异步编程的目的是减少 IO 密集)。

CPU 密集型的代码通常是对程序中的对象进行的运算或其他操作。解析 CSV 文件、映射对象和计算平均值都依赖于 CPU。在处理数据时,效率来自于根据可用的 CPU 数量拆分数据集,基本上在每个 CPU 上同时运行程序--只是处理的是整个数据集不同的分组。

当代码的执行依赖于通过“网线”发送或接收数据时,即通过互联网或内部网络连接到另一个服务器时,代码就是 IO 密集型的。这种情况在调用 API 或数据库的存储库方法中最为常见。如果代码向持久性存储(如硬盘或固态硬盘)写入或读取,那么它也可以是 IO 密集型的。如果我们使用的第三方 API 需要 10 秒才能将数据返回给我们的应用程序,我们也没有什么办法,然而通过异步编程,我们至少可以让我们的程序在等待 API 调用的时候继续使用 CPU 运行其他部分,而不是闲置。

在处理每个数据集都依赖于 IO 密集型调用的情况下,比如调用数据库对处理后的数据进行 INSERT,那么我们运行程序的 CPU 线程数与调用外部源(如 API 或数据库)的次数之间的平衡就显得非常重要。如果 API 有使用限制(例如每秒 10 个请求),或者数据库服务器没有足够的线程来处理我们的程序比如说 20 个线程都试图同时向同一个数据库 insert,那么这一点就尤其重要。

在不使程序的 IO 密集型过载的情况下最大化效率和最小化程序运行时间,可能需要对程序在 ETL 过程的各个步骤中使用的线程数量进行一些试错式的调整。

Parallel.ForEach vs PLINQ

当使用 .NET 时,现在可以通过一个简单的 Parallel.ForEach 循环或 Parallel LINQ[3](PLINQ)来实现并行化应用程序所需的一切。对于 .NET 来说,这些并不是特别新的东西,但与它们的前身相比,它们更容易使用,因为它们需要创建和管理线程,并手动分割集合。Parallel.ForEach 和 PLINQ 两者都为我们处理了这一切,根据我的经验,两者之间的性能没有明显的差异。我猜测它们在底层调用的代码大致相同。

下面是分别使用 Parallel.ForEach 和 PLINQ 读取 CSV 文件的示例:

// PLINQ using all CPU cores
public static void PLINQAll(string filePath)
{var sw = new Stopwatch();sw.Start();var results = System.IO.File.ReadAllLines(filePath).AsParallel().Select(line => Regex.Split(line, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)")).ToList();sw.Stop();Console.WriteLine($"PLINQ using all cores: completed in {Math.Round(sw.Elapsed.TotalSeconds)} seconds");
}// PLINQ using all CPU threads (2x cores)
public static void PLINQAllThreads(string filePath)
{var threads = Environment.ProcessorCount * 2;var sw = new Stopwatch();sw.Start();var results = System.IO.File.ReadAllLines(filePath).AsParallel().WithDegreeOfParallelism(threads).Select(line => Regex.Split(line, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)")).ToList();sw.Stop();Console.WriteLine($"PLINQ using all THREADS {(threads)}: completed in {Math.Round(sw.Elapsed.TotalSeconds)} seconds");
}// PLINQ using 2 CPU cores
public static void PLINQ2(string filePath)
{var sw = new Stopwatch();sw.Start();var results = System.IO.File.ReadAllLines(filePath).AsParallel().WithDegreeOfParallelism(2).Select(line => Regex.Split(line, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)")).ToList();sw.Stop();Console.WriteLine($"PLINQ using 2 cores: completed in {Math.Round(sw.Elapsed.TotalSeconds)} seconds");
}// PLINQ using user-defined thread count
public static void PLINQUser(string filePath, int numThreads)
{var sw = new Stopwatch();sw.Start();var results = System.IO.File.ReadAllLines(filePath).AsParallel().WithDegreeOfParallelism(numThreads).Select(line => Regex.Split(line, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)")).ToList();sw.Stop();Console.WriteLine($"PLINQ using {numThreads} threads: completed in {Math.Round(sw.Elapsed.TotalSeconds)} seconds");
}// PLINQ using all CPU cores without Regex parsing - May yield bad data due to commas within fields
public static void PLINQNoRegex(string filePath)
{var sw = new Stopwatch();sw.Start();var results = System.IO.File.ReadAllLines(filePath).AsParallel().Select(x => x.Split(',')).ToList();sw.Stop();Console.WriteLine($"PLINQ using all cores (no Regex): completed in {Math.Round(sw.Elapsed.TotalSeconds)} seconds");
}// Parallel.ForEach using all CPU cores - May yield bad data
public static void ParallelForEach(string filePath)
{var sw = new Stopwatch();sw.Start();var rows = new List<string[]>();Parallel.ForEach(File.ReadLines(filePath), line =>{rows.Add(line.Split(','));});sw.Stop();Console.WriteLine($"Parallel.ForEach using all cores (no Regex): completed in {Math.Round(sw.Elapsed.TotalSeconds)} seconds");
}// Parallel.ForEach using all CPU cores with Regex parsing - takes roughly the same amount of time as PLINQ
public static void ParallelForEachRegex(string filePath)
{var sw = new Stopwatch();sw.Start();var rows = new List<string[]>();Parallel.ForEach(File.ReadLines(filePath), line =>{rows.Add(Regex.Split(line, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)"));});sw.Stop();Console.WriteLine($"Parallel.ForEach w/Regex using all cores: completed in {Math.Round(sw.Elapsed.TotalSeconds)} seconds");
}

那么应该选择哪一种方式呢?我建议根据你在非并行情况下使用哪种方法来决定--你会使用 foreach 循环还是 LINQ 查询?也就是说,这两者之间最大的、不明显的区别是 Parallel.ForEach 允许你指定线程数,最多不超过计算机或服务器上可用的线程数(你可以指定更多的线程数,但它最多只能启动 CPU 所拥有的线程数)。然而,根据你的指定,PLINQ 可以使用超过计算机上 CPU 线程数的线程(译注:不是所有的线程都是工作中的,所以理论上可以创建无数个线程)。一般情况下,你不会希望启动超出比 CPU 线程更多的线程,但在某些情况下,这样做会更有效。例如,如果你要写一个网络爬虫,那么启动双倍的线程数量可能是有意义的,因为每个线程大概都要等待网站的加载。其他这样的网络密集型(IO 密集型的一个子集)任务可能会在更多线程的情况下运行得更快。

Parallel.ForEach 和 PLINQ 之间还有一些其他的区别,在这里[4]进行了讨论。但是对于 ETL 来说,除非在非常特殊的情况下,这些区别不太适用。例如,如果必须保留数据的顺序,你应该使用 PLINQ,因为它提供了一种保留顺序的方法。

示例程序

我们的 ELT 示例程序(仓库地址[5])将做以下四件事件:

  1. 读取 CSV 文件;

  2. 将这些 CSV 文件中的字段映射到 C# 对象;

  3. 对数据(对象列表)执行一些转换操作;

  4. 将这些数据插入到数据库中。

我将在接下来即将发布的第二篇文章中继续介绍该示例。

文中链接:

[1]. https://dotnet.microsoft.com/apps/data/spark

[2]. https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/potential-pitfalls-in-data-and-task-parallelism

[3]. https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/introduction-to-plinq

[4]. https://devblogs.microsoft.com/pfxteam/when-to-use-parallel-foreach-and-when-to-use-plinq/

[5]. https://gitlab.com/jspinella/parallel-etl-examples

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

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

相关文章

一个小技巧助您减少if语句的状态判断

在进行项目的开发的过程中&#xff0c; if 语句是少不了的&#xff0c;但我们始终要有一颗消灭 if / else 语句的心。为了消灭if / else 我们引入了 短路器 的概念。短路器 有时候的确能精简我们的代码&#xff0c;但还不够&#xff0c;因此我参考了一个方法来继续消灭一部分 断…

抢先看:笔者亲历的2020年中国.NET开发者大会活动纪实

编者&#xff1a;2020年中国.NET开发者大会第一天活动已经结束&#xff0c;可以通过https://codechina.csdn.net/lives 会看。第二天的Workshop 也有直播哦。12020年12月19日的苏州工业园区&#xff0c;天公作美&#xff0c;阳光明媚&#xff0c;气象迷人&#xff0c;正是一个搞…

python魔术方法由谁定义_Python的魔术方法

魔术方法就是在定义的类中定义一些”不一般”的方法&#xff0c;使类的使用更方便、完善、健壮&#xff0c;是python特有的方法&#xff0c;一般都是前后包含两个下划线__的方法称为魔术方法&#xff0c;例如__new__。基本魔术方法有哪些__new__&#xff1a;是在一个对象实例化…

Swagger在header中添加token

概述平常做项目使用mvcwebapi&#xff0c;采取前后端分离的方式&#xff0c;后台提供API接口给前端开发人员。这个过程中遇到一个问题后台开发人员怎么提供接口说明文档给前端开发人员。为了解决这个问题&#xff0c;项目中引用swagger&#xff08;我比较喜欢戏称为“丝袜哥”&…

如何在 C# 中使用 数据注解

数据注解 是一种可以应用到 类 或者 类成员上用来指定类之间关系的一种 Attribute&#xff0c;它的应用场景比较多&#xff0c;可用来描述 UI 上如何进行数据展示&#xff0c;还可以用来做类属性的规则验证&#xff0c;这篇文章就来讨论为什么 注解 值得你去学习&#xff0c;以…

2020年中国.NET开发者大会第二天 WorkShop

工作坊1&#xff1a;使用 NCF 从 0 到 1 快速模块化开发/部署业务系统实战工作坊简介&#xff1a;本次工作坊由盛派开发团队亲自带领开发者使用 NCF&#xff08;NeuCharFramework&#xff09; 框架进行系统快速开发&#xff0c;进行现场实操训练&#xff0c;大家可以通过本次活…

mysql直接生成excel_MYSQL 将excel里面的数据直接生成sql语句

如何使用EXCEL生成SQL语句&#xff1f;将光标放到新的列上里面&#xff0c;然后在公式栏里面输入如下公式&#xff1a;"insert into t values("&A1&","&B1&","&C1&","&D1&")"效果图&#x…

OrchardCore实现模块化核心原理分析

【导读】ABP vNext并未过多探究&#xff0c;当然其基于DDD理念分层清晰&#xff0c;灵活性、扩展性自然也不在话下&#xff0c;但有些情况下我可能会首选OrchardCore&#xff0c;并非ABP vNext不可若改造项目&#xff0c;也因历史遗留问题&#xff0c;数据库表设计也可能存在不…

.NET 云原生架构师训练营(模块二 基础巩固 配置)--学习笔记

2.2.3 核心模块--配置IConfigurationOptionsASP.NET Core 中的配置&#xff1a;https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/configuration/?viewaspnetcore-5.0IConfigurationIConfiguration 的使用层级对象配置到 key-value 键值对转换通过环境变量修改日志…

.NET Conf 2020 - 基于ASP.NET Core构建可热插拔的插件化系统

文章标题&#xff1a;.NET Conf 2020 - 基于ASP.NET Core构建可热插拔的插件化系统作者&#xff1a;Lamond Lu项目地址&#xff1a;https://github.com/lamondlu/CoolCat博客&#xff1a;http://www.cnblogs.com/lwqlun以下是2020.12.19日的演讲文稿和视频&#xff1a;大家好&a…

多款主流编程语言,哪款开发软件最安全?

喜欢就关注我们吧&#xff01;在当下的市场环境中&#xff0c;除了掌握困扰软件的最常见安全问题外&#xff0c;开发人员还应该了解到底是什么问题在影响他们正在使用的编程语言。静态代码分析安全公司 Veracode 最近发布了一份年度软件安全状态&#xff08;SOSS&#xff09;报…

2020年终回顾:时间会回答成长,成长会回答梦想

前言2020年是脚踏实地&#xff0c;慢慢成长的一年&#xff0c;由于疫情的缘故&#xff0c;今年社区没有像去年一样举办多场线下活动&#xff0c;不过 .NET CONF CHINA 大会昨天也在苏州顺利召开&#xff0c;回顾这一年&#xff0c;也有不少惊喜与感悟2020年回顾公众号自从去年双…

java servlet 跳转_Servlet跳转方式sendReDirect()和forward()

在web应用服务中&#xff0c;经常会面对不同SERVLET之间的跳转&#xff0c;目前我们可以通过以下两种方式实现&#xff1a;1.RequestDispatcher.forward()2.ServletResponse.sendReDirect()两者的区别&#xff1a;1.redirect 方式可以跨应用访问,forward 只能在同一个应用中跳转…

明天面腾讯,我刷了这71道面试题...

激动人心的Conf 2020中国.NET开发者大会完美落幕&#xff0c;有幸去到现场&#xff0c;跟诸位.NET大佬、微软大咖、MVP面对面交流&#xff0c;内心很是鸡冻&#xff01;聊天中我注意到一个细节&#xff0c;很多公司的项目都在逐步用MySQL替换SQLServer&#xff0c;尤其是微服务…

java字符串切分_Java字符串分割(转)

java.lang.String的split()方法, JDK 1.4 or laterpublic String[] split(String regex,int limit)示例代码public class StringSplit {public static void main(String[] args) {String sourceStr "1,2,3,4,5";String[] sourceStrArray sourceStr.split(",&q…

面试官:. NET5源码里用到了哪些设计模式?懵!

作为微软最早迈向开源的重要软件之一&#xff0c;.NET 5的发布具有重要意义&#xff01;微软希望 .NET Framework 开发者能够迁移他们的代码和应用到 .NET 5.0 上&#xff0c;为明年发布的 .NET 6.0 将 Xamarin 开发者过渡到统一平台奠定基础。版本发布时间轴&#xff1a;.NET …

C# 中 ConcurrentDictionary 一定线程安全吗?

根据 .NET 官方文档的定义&#xff1a;ConcurrentDictionary<TKey,TValue> Class 表示可由多个线程同时访问的线程安全的键/值对集合。这也是我们在并发任务中比较常用的一个类型&#xff0c;但它真的是绝对线程安全的吗&#xff1f;仔细阅读官方文档&#xff0c;我们会发…

2020 .NET 开发者峰会顺利在苏州落幕,相关数据很喜人以及线上直播回看汇总

在2019年上海中国.NET开发者大会的基础上&#xff0c;2020年12月19-20日 继续以“开源、共享、创新” 为主题的第二届中国 .NET 开发者峰会&#xff08;.NET Conf China 2020&#xff09;在苏州人工智能智能产业创新中心落下帷幕&#xff0c;本次大会以线下城市苏州为中心&…

.NET 云原生架构师训练营(模块二 基础巩固 REST RESTful)--学习笔记

2.3.1 Web API -- REST && RESTful什么是 REST&#xff0c;什么是 RESTfulRESTful API 设计RESTful 成熟度模型什么是 REST&#xff0c;什么是 RESTful理解RESTful架构&#xff1a;https://www.ruanyifeng.com/blog/2011/09/restful.htmlREST&#xff08;Representatio…

vue 一个组件内多个弹窗_论如何用Vue实现一个弹窗-一个简单的组件实现

前言最近在使用element-ui框架&#xff0c;用到了Dialog对话框组件&#xff0c;大致实现的效果&#xff0c;跟我之前自己在移动端项目里面弄的一个弹窗组件差不太多。然后就想着把这种弹窗组件的实现方式与大家分享一下&#xff0c;下面本文会带着大家手摸手实现一个弹窗组件。…