首页 文章 精选 留言 我的

精选列表

搜索[快速入门],共10000篇文章
优秀的个人博客,低调大师

「Spark从精通到重新入门(一)」Spark 中不可不知的动态优化

前言 Apache Spark 自 2010 年面世,到现在已经发展为大数据批计算的首选引擎。而在 2020 年 6 月份发布的Spark 3.0 版本也是 Spark 有史以来最大的 Release,其中将近一半的 issue 都属于 SparkSQL。这也迎合我们现在的主要场景(90% 是 SQL),同时也是优化痛点和主要功能点。我们 Erda 的 FDP 平台(Fast Data Platform)也从 Spark 2.4 升级到 Spark 3.0 并做了一系列的相关优化,本文将主要结合 Spark 3.0 版本进行探讨研究。 ​ 为什么 Spark 3.0 能够“神功大成”,在速度和性能方面有质的突破?本文就为大家介绍 Spark 3.0 中 SQL Engine 的“天榜第一”——自适应查询框架 AQE(Adaptive Query Execution)。 AQE,你是谁? ​ 简单来说,自适应查询就是在运行时不断优化执行逻辑。 ​ Spark 3.0 版本之前,Spark 执行 SQL 是先确定 shuffle 分区数或者选择 Join 策略后,再按规划执行,过程中不够灵活;现在,在执行完部分的查询后,Spark 利用收集到结果的统计信息再对查询规划重新进行优化。这个优化的过程不是一次性的,而是随着查询会不断进行优化, 让整个查询优化变得更加灵活和自适应。这一改动让我们告别之前无休止的被动优化。 ​ AQE,你会啥? ​ 了解了 AQE 是什么之后,我们再看看自适应查询 AQE 的“三板斧”: ​ 动态合并 Shuffle 分区 动态调整 Join 策略 动态优化数据倾斜 动态合并 shuffle 分区 ​ 如果你之前使用过 Spark,也许某些“调优宝典”会告诉你调整 shuffle 的 partitions 数量,默认是 200。但是在不同 shuffle 中,数据的大小和分布基本都是不同的,那么简单地用一个配置,让所有的 shuffle 来遵循,显然不是最优的。 ​ 分区过小会导致每个 partition 处理的数据较大,可能需要将数据溢写到磁盘,从而减慢查询速度;分区过大又会带来 GC 压力和低效 I/O 等问题。因此,动态合并 shuffle 分区是非常必要的。AQE 可以在运行期间动态调整分区数来达到性能最优。 ​ 如下图所示,如果没有 AQE,shuffle 分区数为 5,对应执行的 Task 数为 5,但是其中有三个的数据量很少,任务分配不平衡,浪费了资源,降低了处理效率。 ​ 而 AQE 会合并三个小分区,最终只执行三个 Task,这样就不会出现之前 Task 空转的资源浪费情况。 动态调整 join 策略 ​ SparkJoin 策略大致可以分三种,分别是 Broadcast Hash Join、Shuffle Hash Join 和 SortMerge Join。其中 Broadcast 通常是性能最好的,Spark 会在执行前选择合适的 Join 策略。 ​ 例如下面两个表的大小分别为 100 MB 和 30 MB,小表超过 10 MB (spark.sql.autoBroadcastJoinThreshold = 10 MB),所以在 Spark 2.4 中,执行前就选择了 SortMerge Join 的策略,但是这个方案并没有考虑 Table2 经过条件过滤之后的大小实际只有 8 MB。 ​ AQE 可以基于运行期间的统计信息,将 SortMerge Join 转换为 Broadcast Hash Join。 ​ 在上图中,Table2 经过条件过滤后真正参与 Join 的数据只有 8 MB,因此 Broadcast Hash Join 策略更优,Spark 3.0 会及时选择适合的 Join 策略来提高查询性能。 ​ 动态优化数据倾斜 数据倾斜一直是我们数据处理中的常见问题。当将相同 key 的数据拉取到一个 Task 中处理时,如果某个 key 对应的数据量特别大的话,就会发生数据倾斜,如下图一样产生长尾任务导致整个 Stage 耗时增加甚至 OOM。之前的解决方法比如重写 query 或者增加 key 消除数据分布不均,都非常浪费时间且后期难以维护。 ​ AQE 可以检查分区数据是否倾斜,如果分区数据过大,就将其分隔成更小的分区,通过分而治之来提升总体性能。 ​ 没有 AQE 倾斜优化时,当某个 shuffle 分区的数据量明显高于其他分区,会产生长尾 Task,因为整个 Stage 的结束时间是按它的最后一个 Task 完成时间计算,下一个 Stage 只能等待,这会明显降低查询性能。 ​ 开启 AQE 后,会将 A0 分成三个子分区,并将对应的 B0 复制三份,优化后将有 6 个 Task 运行 Join,且每个 Task 耗时差不多,从而获得总体更好的性能。通过对倾斜数据的自适应重分区,解决了倾斜分区导致的整个任务的性能瓶颈,提高了查询处理效率。 自适应查询 AQE 凭借着自己的“三板斧”,在 1TB TPC-DS 基准中,可以将 q77 的查询速度提高 8 倍,q5 的查询速度提高 2 倍,且对另外 26 个查询的速度提高 1.1 倍以上,这是普通优化无法想象的傲人战绩! ​ 真的吗?我不信 口说无凭,自适应查询 AQE 的优越性到底是如何实现,我们“码”上看看。 ​ AQE 参数说明 #AQE开关 spark.sql.adaptive.enabled=true #默认false,为true时开启自适应查询,在运行过程中基于统计信息重新优化查询计划 spark.sql.adaptive.forceApply=true #默认false,自适应查询在没有shuffle或子查询时将不适用,设置为true将始终使用 spark.sql.adaptive.advisoryPartitionSizeInBytes=64M #默认64MB,开启自适应执行后每个分区的大小。合并小分区和分割倾斜分区都会用到这个参数 #开启合并shuffle分区 spark.sql.adaptive.coalescePartitions.enabled=true #当spark.sql.adaptive.enabled也开启时,合并相邻的shuffle分区,避免产生过多小task spark.sql.adaptive.coalescePartitions.initialPartitionNum=200 #合并之前shuffle分区数的初始值,默认值是spark.sql.shuffle.partitions,可设置高一些 spark.sql.adaptive.coalescePartitions.minPartitionNum=20 #合并后的最小shuffle分区数。默认值是Spark集群的默认并行性 spark.sql.adaptive.maxNumPostShufflePartitions=500 #reduce分区最大值,默认500,可根据资源调整 #开启动态调整Join策略 spark.sql.adaptive.join.enabled=true #与spark.sql.adaptive.enabled都开启的话,开启AQE动态调整Join策略 #开启优化数据倾斜 spark.sql.adaptive.skewJoin.enabled=true #与spark.sql.adaptive.enabled都开启的话,开启AQE动态处理Join时数据倾斜 spark.sql.adaptive.skewedPartitionMaxSplits=5 #处理一个倾斜Partition的task个数上限,默认值为5; spark.sql.adaptive.skewedPartitionRowCountThreshold=1000000 #倾斜Partition的行数下限,即行数低于该值的Partition不会被当作倾斜,默认值一千万 spark.sql.adaptive.skewedPartitionSizeThreshold=64M #倾斜Partition的大小下限,即大小小于该值的Partition不会被当做倾斜,默认值64M spark.sql.adaptive.skewedPartitionFactor=5 #倾斜因子,默认为5。判断是否为倾斜的 Partition。如果一个分区(DataSize>64M*5) || (DataNum>(1000w*5)),则视为倾斜分区。 spark.shuffle.statistics.verbose=true #默认false,打开后MapStatus会采集每个partition条数信息,用于倾斜处理 AQE 功能演示 ​ Spark 3.0 默认未开启 AQE 特性,样例 sql 执行耗时 41 s。 ​ 存在 Task 空转情况,shuffle 分区数始终为默认的 200。 ​ 开启 AQE 相关配置项,再次执行样例 sql。 ​ 样例 sql 执行耗时 18 s,快了一倍以上。 ​ 并且每个 Stage 的分区数动态调整,而不是固定的 200。无 task 空转情况,在 DAG 图中也能观察到特性开启。 ​ 总结 Spark 3.0 在速度和性能方面得提升有目共睹,它的新特性远不止自适应查询一个,当然也不意味着所有的场景都能有明显的性能提升,还需要我们结合业务和数据进行探索和使用。 ​ 注:文中部分图片源自于网络,侵删。

优秀的个人博客,低调大师

Python爬虫入门教程 15-100 石家庄政民互动数据爬取

1. 石家庄政民互动数据爬取-写在前面 今天,咱抓取一个网站,这个网站呢,涉及的内容就是 网友留言和回复,特别简单,但是网站是gov的。网址为http://www.sjz.gov.cn/col/1490066682000/index.html 首先声明,为了学习,绝无恶意抓取信息,不管你信不信,数据我没有长期存储,预计存储到重装操作系统就删除。 2. 石家庄政民互动数据爬取-网页分析 点击更多回复 ,可以查看到相应的数据。 数据量很大14万条,,数据爬完,还可以用来学习数据分析,真是nice 经过分析之后,找到了列表页面。数据的爬取这次我们采用的是 selenium ,解析页面采用lxml,数据存储采用pymongo ,关于selenium 你可以去搜索引擎搜索相关的教程,好多的,主要就是打开一个浏览器,然后模拟用户的操作,你可以去系统的学习一下。 3. 石家庄政民互动数据爬取-撸代码 导入必备模块 from selenium import webdriver from selenium.common.exceptions import TimeoutException from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from lxml import etree import pymongo import time 打开浏览器,获取总页码 这个操作最重要的步骤,你搜索之后就会知道,需要提前下载一个叫做 chromedriver.exe 的东东,然后把他配置好,自行解决去吧~ # 加载浏览器引擎,需要提前下载好 chromedriver.exe 。 browser = webdriver.Chrome() wait = WebDriverWait(browser,10) def get_totle_page(): try: # 浏览器跳转 browser.get("http://www.sjz.gov.cn/zfxxinfolist.jsp?current=1&wid=1&cid=1259811582187") # 等待元素加载到 totle_page = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR,'input[type="hidden"]:nth-child(4)')) ) # 获取属性 totle = totle_page.get_attribute('value') # 获取首页数据,这个地方先不必须 ############################## #get_content() ############################## return totle except TimeoutError: return get_totle_page() 上面的代码在测试之后,你会得到如下结果 这时候,你已经得到20565这个总页码数目了,只需要进行一系列循环的操作即可,接下来有一个重要的函数,叫做next_page 这个函数里面,需要进行一个模拟用户行为的操作,输入一个页码,然后点击跳转。 def main(): totle = int(get_totle_page()) # 获取完整页码 for i in range(2,totle+1): print("正在加载第{}页数据".format(i)) # 获取下一页 next_page(i) if __name__ == '__main__': print(main()) 输入页码,点击跳转 def next_page(page_num): try: input = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR,"#pageto")) ) submit = wait.until( EC.element_to_be_clickable((By.CSS_SELECTOR,"#goPage")) ) input.clear() # 清空文本框 input.send_keys(page_num) # 发送页码 submit.click() # 点击跳转 #get_content(page_num) except TimeoutException: next_page(page_num) 以上代码实现的效果动态演示为 4. 石家庄政民互动数据爬取-解析页面 可以进行翻页之后,通过browser.page_source 获取网页源码,网页源码通过lxml进行解析。编写相应的方法为 def get_content(page_num=None): try: wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, "table.tably")) ) html = browser.page_source # 获取网页源码 tree = etree.HTML(html) # 解析 tables = tree.xpath("//table[@class='tably']") for table in tables: name = table.xpath("tbody/tr[1]/td[1]/table/tbody/tr[1]/td")[0].text public_time = table.xpath("tbody/tr[1]/td[1]/table/tbody/tr[2]/td")[0].text to_people = table.xpath("tbody/tr[1]/td[1]/table/tbody/tr[3]/td")[0].text content = table.xpath("tbody/tr[1]/td[2]/table/tbody/tr[1]/td")[0].text repl_time = table.xpath("tbody/tr[2]/td[1]/table/tbody/tr[1]/td")[0].text repl_depart = table.xpath("tbody/tr[2]/td[1]/table/tbody/tr[2]/td")[0].text repl_content = table.xpath("tbody/tr[2]/td[2]/table/tbody/tr[1]/td")[0].text # 清理数据 consult = { "name":name.replace("网友:",""), "public_time":public_time.replace("时间:",""), "to_people":to_people.replace("留言对象:",""), "content":content, "repl_time":repl_time.replace("时间:",""), "repl_depart":repl_depart.replace("回复部门:",""), "repl_content":repl_content } # 数据存储到mongo里面 #save_mongo(consult) except Exception: # 这个地方需要特殊说明一下 print("异常错误X1") print("浏览器休息一下") time.sleep(60) browser.get("http://www.sjz.gov.cn/zfxxinfolist.jsp?current={}&wid=1&cid=1259811582187".format(page_num)) get_content() 在实际的爬取过程中发现,经过几百页之后,就会限制一下IP,所以当我们捕获页面信息出错,需要暂停一下,等待页面正常之后,在继续爬取数据。 5. 石家庄政民互动数据爬取-数据存储到mongodb里面 爬取到的最终数据,我存储到了mongodb里面,这个就没有什么难度了,我们按照常规的套路编写即可。 6. 石家庄政民互动数据爬取-写在最后 由于这次爬取的网站是gov的,所以建议不要用多线程,源码也不发送到github上去了,要不惹祸,如果有任何疑问,请评论。nice boy她专科学历27岁从零开始学习c,c++,python编程语言29岁编写百例教程30岁掌握10种编程语言,用自学的经历告诉你,学编程就找梦想橡皮擦

优秀的个人博客,低调大师

Python爬虫入门教程 7-100 蜂鸟网图片爬取之二

1. 蜂鸟网图片-简介 今天玩点新鲜的,使用一个新库 aiohttp ,利用它提高咱爬虫的爬取速度。 安装模块常规套路 pip install aiohttp 运行之后等待,安装完毕,想要深造,那么官方文档必备 :https://aiohttp.readthedocs.io/en/stable/ 接下来就可以开始写代码了。 我们要爬取的页面,这一次选取的是 http://bbs.fengniao.com/forum/forum_101_1_lastpost.html 打开页面,我们很容易就获取到了页码 好久没有这么方便的看到页码了。 尝试用 aiohttp 访问这个页面吧,模块的引入,没有什么特殊的,采用 import 即可 如果我们需要 使用 Asyncio + Aiohttp异步IO 编写爬虫,那么需要注意,你需要异步的方法前面加上 async 接下来

优秀的个人博客,低调大师

Python爬虫入门教程 8-100 蜂鸟网图片爬取之三

1. 蜂鸟网图片-啰嗦两句 前几天的教程内容量都比较大,今天写一个相对简单的,爬取的还是蜂鸟,依旧采用aiohttp 希望你喜欢爬取页面https://tu.fengniao.com/15/ 本篇教程还是基于学习的目的,为啥选择蜂鸟,没办法,我瞎选的。 一顿熟悉的操作之后,我找到了下面的链接https://tu.fengniao.com/ajax/ajaxTuPicList.php?page=2&tagsId=15&action=getPicLists 这个链接返回的是JSON格式的数据 page =2页码,那么从1开始进行循环就好了 tags=15 标签名称,15是儿童,13是美女,6391是私房照,只能帮助你到这了,毕竟我这是专业博客 ヾ(◍°∇°◍)ノ゙ action=getPicLists接口地址,不变的地方 2.

优秀的个人博客,低调大师

Python爬虫入门教程 6-100 蜂鸟网图片爬取之一

1. 蜂鸟网图片简介 国庆假日结束了,新的工作又开始了,今天我们继续爬取一个网站,这个网站为 http://image.fengniao.com/ ,蜂鸟一个摄影大牛聚集的地方,本教程请用来学习,不要用于商业目的,不出意外,蜂鸟是有版权保护的网站。 2. 蜂鸟网图片网站分析 第一步,分析要爬取的网站有没有方法爬取,打开页面,找分页 http://image.fengniao.com/index.php?action=getList&class_id=192&sub_classid=0&page=1&not_in_id=5352384,5352410 http://image.fengniao.com/index.php?action=getList&class_id=192&sub_classid=0&page=2&not_in_id=5352384,5352410 http://image.fengniao.com/index.php?action=getList&class_id=192&sub_classid=0&page=3&not_in_id=5352384,5352410 http://image.fengniao.com/index.php?action=getList&class_id=192&sub_classid=0&page=4&not_in_id=5352384,5352410 上面的页面发现一个关键的参数page=1这个就是页码了,但是另一个比较头疼的问题是,他没有最后的页码,这样我们没有办法确定循环次数,所以后面的代码编写中,只能使用while了 这个地址返回的是JSON格式的数据,这个对爬虫来说,非常友好!省的我们用正则表达式分析了。 分析这个页面的头文件,查阅是否有反爬措施 发现除了HOST和User-Agent以外,没有特殊的点,大网站就是任性,没啥反爬,可能压根不在乎这个事情。 第二步,分析图片详情页面,在我们上面获取到的JSON中,找到关键地址 关键地址打开之后,这个地方有一个比较骚的操作了,上面图片中标注的URL选的不好,恰好是一个文章了,我们要的是组图,重新提供一个新链接 http://image.fengniao.com/slide/535/5352130_1.html#p=1 打开页面,你可能直接去找规律了,找到下面的一堆链接,但是这个操作就有点复杂了,我们查阅上述页面的源码 http://image.fengniao.com/slide/535/5352130_1.html#p=1 http://image.fengniao.com/slide/535/5352130_1.html#p=2 http://image.fengniao.com/slide/535/5352130_1.html#p=3 .... 网页源码中发现了,这么一块区域 大胆的猜测一下,这个应该是图片的JSON,只是他打印在了HTML中,我们只需要用正则表达式进行一下匹配就好了,匹配到之后,然后进行下载。 第三步,开始撸代码。 3. 蜂鸟网图片写代码 from http_help import R # 这个文件自己去上篇博客找,或者去github找 import threading import time import json import re img_list = [] imgs_lock = threading.Lock() #图片操作锁 # 生产者类 class Product(threading.Thread): def __init__(self): threading.Thread.__init__(self) self.__headers = {"Referer":"http://image.fengniao.com/", "Host": "image.fengniao.com", "X-Requested-With":"XMLHttpRequest" } #链接模板 self.__start = "http://image.fengniao.com/index.php?action=getList&class_id=192&sub_classid=0&page={}&not_in_id={}" self.__res = R(headers=self.__headers) def run(self): # 因为不知道循环次数,所有采用while循环 index = 2 #起始页码设置为1 not_in = "5352384,5352410" while True: url = self.__start.format(index,not_in) print("开始操作:{}".format(url)) index += 1 content = self.__res.get_content(url,charset="gbk") if content is None: print("数据可能已经没有了====") continue time.sleep(3) # 睡眠3秒 json_content = json.loads(content) if json_content["status"] == 1: for item in json_content["data"]: title = item["title"] child_url = item["url"] # 获取到链接之后 代码来源,公众号:非本科程序员 img_content = self.__res.get_content(child_url,charset="gbk") pattern = re.compile('"pic_url_1920_b":"(.*?)"') imgs_json = pattern.findall(img_content) if len(imgs_json) > 0: if imgs_lock.acquire(): img_list.append({"title":title,"urls":imgs_json}) # 这个地方,我用的是字典+列表的方式,主要是想后面生成文件夹用,你可以进行改造 imgs_lock.release() 上面的链接已经生成,下面就是下载图片了,也非常简单 # 消费者 class Consumer(threading.Thread): def __init__(self): threading.Thread.__init__(self) self.__res = R() def run(self): while True: if len(img_list) <= 0: continue # 进入下一次循环 if imgs_lock.acquire(): data = img_list[0] del img_list[0] # 删除第一项 imgs_lock.release() urls =[url.replace("\\","") for url in data["urls"]] # 创建文件目录 for item_url in urls: try: file = self.__res.get_file(item_url) # 记得在项目根目录先把fengniaos文件夹创建完毕,代码来源,公众号:非本科程序员 with open("./fengniaos/{}".format(str(time.time())+".jpg"), "wb+") as f: f.write(file) except Exception as e: print(e) 代码走起,结果

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册