首页 文章 精选 留言 我的

精选列表

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

【web前端优化之图片模糊到清晰】看我QQ空间如何显示相片

前言 此篇文章估计不会太长,有移除首页的风险,但是老夫(称老夫是因为我们真正的叶小钗其实都100多岁啦)是不会怕滴。所以,我来了哟! 题外话:今天我们一起还看了一道前端的面试题,而后我本来还想多找几道来做做,但是没找到什么合适的,各位有什么好的前端面试题请给我留言哦,我们一起来分析面试题进步哟! 前端时间,我与我们的产品有一次讨论,是针对图片压缩的,因为我对图片或者说PS这块是个小白,所以当时做了一个广告图片有100来k也直接给传上去了,结果被我们的同事搞起来一压缩,便只有50多k了,此次交流对图片压缩这块有了一点点心得,并且为第二次交流埋下伏笔,第二次便是图片由模糊变清晰的研究了。 网页图片格式 此处我先对我们的图片格式做一下普及吧,参考: 【整理】详说JPG,GIF及PNG各类型的图片格式 GIF 透明类型,可以全透明或者全不透明,半透明这种事情就不要找他了,并且这个家伙可以做动画哦 gif是一种无损耗的图片格式 gif采用lzw算法进行压缩,当压缩gif过程中像素由上到下进行压缩,也就是说横向的gif图片比纵向的小(500*10比10*500小) gif支持可选择性的间隔渐进显示 JPEG 不支持透明 不支持动画 该图片非常容易损耗 支持隔行渐进显示(ie不支持,ie会再整个图片信息完全到达后再显示) jepg尤其适合web上面的摄影图片和数字相册 PNG 支持各种透明,但在IE6下有bug需要使用滤镜处理 不支持动画 任何操作都不会损耗其质量 支持间隔渐进增强,但会造成图片尺寸变大: 复制代码 png8(布尔透明) 相当于静态gif,只有256色,支持索引透明,就是指定一个像素点不是透明 png8(alpha透明) 可指定像素点的透明度,例如50%透明度 优点在于比png24/32小,效果一样,缺点为ie6不支持 png24 不透明,颜色很多不止256色,PS导出的png24事实上为png32 png32 和PS里面的PSD一样,包含图层和通道信息 复制代码 以上是关于图片的一些信息,我们大概了解下便是,其中我要说一说其中的PNG,特别是交错png PNG交错在使用浏览器欣赏该图片时就会以由模糊逐渐转为清晰的效果方式渐渐显示出来。PNG先进的交错式方法,使图像得以水平及垂直方式显像在屏幕上,加快了下載的速度,作用:交错可使下载时间感觉更短,并使浏览者确信正在进行下载。PNG无交错、不交错就没这个作用。 图片的显示 我们知道img标签在dom加载时候是不会加载的,而是在dom结构全部出来后并形成了渲染树(布局结束),才开始加载。 而其加载顺序也是从上而下的加载,意思是图片我们是先看到上面再看到下面,但是很明显这不是一个好的显示方式,我们若是一开始可以看到模糊的图形然后再慢慢变清晰是不是好很多呢? 肯定好很多啦。。。 于是我们讨论到如何实现,但是就说到了交错PNG,我当时虽然信了,因为我对此不太了解,但是下来思考下却感觉有点不对劲! 用户的疑惑 现在我们来想象下QQ空间的做法(这块纯粹瞎扯),我现在作为一个用户,我上传图片来了 ① 我想将QQ空间作为云存储的地方,上传了我2M的毕业照 ② 空间根据需求生成了一张缩略图与一张大图 ③ 我们首先看到缩略图,而后看到大图,点击原图时候便看到我最初2M的照片 以上是我以为的逻辑,QQ空间是不是这个逻辑我们不去关注他,因为站在用户角度,我一定是想保留我最初的东西。 好了事到如今,QQ空间究竟怎么做的呢???那个太复杂了,我们也不去关注,我这里说下我是怎么做的。 实现图片由模糊到清晰 我们的相册显示一般是这个样子的: 上面是缩略图,下面是大图,在这里我们其实可以对其缩略图做文章!!! 我们在大图显示完之前可以用缩略图“代替”大图吗,来看看我们的逻辑: ① 缩略图在相册上方,其最先加载,就算在下方,因为缩略图尺寸很小加载十分迅速 ② 最初将缩略图放到大图显示位置,将其大小设置为大图大小(此尺寸有多种方法可获取,比如上传时候便计算结束) ② 将大图布局置于缩略图前,因为缩略图已经展示,但是因为过大而显得有点模糊,但大图慢慢加载其由上至下变得清晰给人一种模糊到清晰的错觉 ④ 流程结束 于是我们来看看我们的QQ空间吧,看之前我们用限速工具,给我们的火狐限速: PS:限速后开空间巨慢。。。 怎么样,够慢了的吧! 注意看其由上到下的变化哦,然后我这里找出了证据 PS:我打开网上限制怎么还是很慢,看来是我网速慢的原因啦。。。 请看我这边用红框圈着的三块地方: 第一个为相册显示的容器,relative定位的 第二个为上面的小缩略图,很小的那种哦: 看吧,这里活生生的将人家扯那么大。。。。。 第三个框便是主角,大图啦: 结论 从其整个排布来看,与我们思考的一致,他这样做好处多多的有哦,比如我们这里上面的图片导航: 我们看到上面的缩略图早就显示出来了,点击下一张的时候缩略图会展示出来,不会出现空白的断层,然后大图慢慢的显示让用户愿意停在那里。 结语 好啦,今天我们研究了一道面试题,后面又研究了图片由模糊到清晰的方案,若是您有更好的解决方案不要藏着哦! 好啦,若是你有好的web前端面试题也请留下,我最近在做这方面的整理,后面对各位也会有帮助滴,最后留一张老夫的玉照吧! 本文转自叶小钗博客园博客,原文链接http://www.cnblogs.com/yexiaochai/p/3151662.html,如需转载请自行联系原作者

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

【转】IOS设备旋转的内部处理流程以及一些【优化建议】

加速计是整个IOS屏幕旋转的基础,依赖加速计,设备才可以判断出当前的设备方向,IOS系统共定义了以下七种设备方向: typedefNS_ENUM(NSInteger, UIDeviceOrientation) { UIDeviceOrientationUnknown, UIDeviceOrientationPortrait, // Device oriented vertically, home button on the bottom UIDeviceOrientationPortraitUpsideDown,// Device oriented vertically, home button on the top UIDeviceOrientationLandscapeLeft, // Device oriented horizontally, home button on the right UIDeviceOrientationLandscapeRight, // Device oriented horizontally, home button on the left UIDeviceOrientationFaceUp, // Device oriented flat, face up UIDeviceOrientationFaceDown // Device oriented flat, face down }; 以及如下四种界面方向: typedefNS_ENUM(NSInteger, UIInterfaceOrientation) { UIInterfaceOrientationPortrait =UIDeviceOrientationPortrait, UIInterfaceOrientationPortraitUpsideDown =UIDeviceOrientationPortraitUpsideDown, UIInterfaceOrientationLandscapeLeft =UIDeviceOrientationLandscapeRight, UIInterfaceOrientationLandscapeRight =UIDeviceOrientationLandscapeLeft }; 一、UIKit处理屏幕旋转的流程 当加速计检测到方向变化的时候,会发出UIDeviceOrientationDidChangeNotification通知,这样任何关心方向变化的view都可以通过注册该通知,在设备方向变化的时候做出相应的响应。 UIKit的相应屏幕旋转的流程如下: 1、设备旋转的时候,UIKit接收到旋转事件。 2、UIKit通过AppDelegate通知当前程序的window。 3、Window会知会它的rootViewController,判断该view controller所支持的旋转方向,完成旋转。 4、如果存在弹出的view controller的话,系统则会根据弹出的view controller,来判断是否要进行旋转。 二、UIViewController实现屏幕旋转 在响应设备旋转时,我们可以通过UIViewController的方法实现更细粒度的控制,当view controller接收到window传来的方向变化的时候,流程如下: 1、首先判断当前viewController是否支持旋转到目标方向,如果支持的话进入流程2,否则此次旋转流程直接结束。 2、调用willRotateToInterfaceOrientation:duration:方法,通知view controller将要旋转到目标方向。如果该viewController是一个container viewcontroller的话,它会继续调用其content view controller的该方法。这个时候我们也可以暂时将一些view隐藏掉,等旋转结束以后在现实出来。 3、window调整显示的view controller的bounds,由于view controller的bounds发生变化,将会触发viewWillLayoutSubviews方法。这个时候self.interfaceOrientation和statusBarOrientation方向还是原来的方向。 4、接着当前view controller的willAnimateRotationToInterfaceOrientation:duration:方法将会被调用。系统将会把该方法中执行的所有属性变化放到动animationblock中。 5、执行方向旋转的动画。 6、最后调用didRotateFromInterfaceOrientation:方法,通知view controller旋转动画执行完毕。这个时候我们可以将第二部隐藏的view再显示出来。 整个响应过程如下图所示: 以上就是UIKit下一个完整的屏幕旋转流程,我们只需要按照提示做出相应的处理就可以完美的支持屏幕旋转。 三、注意事项和建议 1)注意事项 当我们的view controller隐藏的时候,设备方向也可能发生变化。例如view Controller A弹出一个全屏的view controller B的时候,由于A完全不可见,所以就接收不到屏幕旋转消息。这个时候如果屏幕方向发生变化,再dismiss B的时候,A的方向就会不正确。我们可以通过在view controller A的viewWillAppear中更新方向来修正这个问题。 2)屏幕旋转时的一些建议 在旋转过程中,暂时界面操作的响应。 旋转前后,尽量保持当前显示的位置不变。 对于view层级比较复杂的时候,为了提高效率在旋转开始前使用截图替换当前的view层级,旋转结束后再将原view层级替换回来。 在旋转后最好强制reload tableview,保证在方向变化以后,新的row能够充满全屏。例如对于有些照片展示界面,竖屏只显示一列,但是横屏的时候显示列表界面,这个时候一个界面就会显示更多的元素,此时reload内容就是很有必要的。 本文转自编程小翁博客园博客,原文链接:http://www.cnblogs.com/wengzilin/p/3258479.html,如需转载请自行联系原作者

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

Spark性能优化之道——解决Spark数据倾斜(Data Skew)的N种姿势

本文结合实例详细阐明了Spark数据倾斜的几种场景以及对应的解决方案,包括避免数据源倾斜,调整并行度,使用自定义Partitioner,使用Map侧Join代替Reduce侧Join,给倾斜Key加上随机前缀等。 为何要处理数据倾斜(Data Skew) 什么是数据倾斜 对Spark/Hadoop这样的大数据系统来讲,数据量大并不可怕,可怕的是数据倾斜。 何谓数据倾斜?数据倾斜指的是,并行处理的数据集中,某一部分(如Spark或Kafka的一个Partition)的数据显著多于其它部分,从而使得该部分的处理速度成为整个数据集处理的瓶颈。 数据倾斜是如何造成的 在Spark中,同一个Stage的不同Partition可以并行处理,而具有依赖关系的不同Stage之间是串行处理的。假设某个Spark Job分为Stage 0和Stage 1两个Stage,且Stage 1依赖于Stage 0,那Stage 0完全处理结束之前不会处理Stage 1。而Stage 0可能包含N个Task,这N个Task可以并行进行。如果其中N-1个Task都在10秒内完成,而另外一个Task却耗时1分钟,那该Stage的总时间至少为1分钟。换句话说,一个Stage所耗费的时间,主要由最慢的那个Task决定。 由于同一个Stage内的所有Task执行相同的计算,在排除不同计算节点计算能力差异的前提下,不同Task之间耗时的差异主要由该Task所处理的数据量决定。 Stage的数据来源主要分为如下两类 从数据源直接读取。如读取HDFS,Kafka 读取上一个Stage的Shuffle数据 如何缓解/消除数据倾斜 尽量避免数据源的数据倾斜 以Spark Stream通过DirectStream方式读取Kafka数据为例。由于Kafka的每一个Partition对应Spark的一个Task(Partition),所以Kafka内相关Topic的各Partition之间数据是否平衡,直接决定Spark处理该数据时是否会产生数据倾斜。 如《Kafka设计解析(一)- Kafka背景及架构介绍》一文所述,Kafka某一Topic内消息在不同Partition之间的分布,主要由Producer端所使用的Partition实现类决定。如果使用随机Partitioner,则每条消息会随机发送到一个Partition中,从而从概率上来讲,各Partition间的数据会达到平衡。此时源Stage(直接读取Kafka数据的Stage)不会产生数据倾斜。 但很多时候,业务场景可能会要求将具备同一特征的数据顺序消费,此时就需要将具有相同特征的数据放于同一个Partition中。一个典型的场景是,需要将同一个用户相关的PV信息置于同一个Partition中。此时,如果产生了数据倾斜,则需要通过其它方式处理。 调整并行度分散同一个Task的不同Key 原理 Spark在做Shuffle时,默认使用HashPartitioner(非Hash Shuffle)对数据进行分区。如果并行度设置的不合适,可能造成大量不相同的Key对应的数据被分配到了同一个Task上,造成该Task所处理的数据远大于其它Task,从而造成数据倾斜。 如果调整Shuffle时的并行度,使得原本被分配到同一Task的不同Key发配到不同Task上处理,则可降低原Task所需处理的数据量,从而缓解数据倾斜问题造成的短板效应。 案例 现有一张测试表,名为student_external,内有10.5亿条数据,每条数据有一个唯一的id值。现从中取出id取值为9亿到10.5亿的共1.5条数据,并通过一些处理,使得id为9亿到9.4亿间的所有数据对12取模后余数为8(即在Shuffle并行度为12时该数据集全部被HashPartition分配到第8个Task),其它数据集对其id除以100取整,从而使得id大于9.4亿的数据在Shuffle时可被均匀分配到所有Task中,而id小于9.4亿的数据全部分配到同一个Task中。处理过程如下 INSERTOVERWRITETABLEtest SELECTCASEWHENid<940000000THEN(9500000+(CAST(RAND()*8ASINTEGER))*12) ELSECAST(id/100ASINTEGER) END, name FROMstudent_external WHEREidBETWEEN900000000AND1050000000; 通过上述处理,一份可能造成后续数据倾斜的测试数据即以准备好。接下来,使用Spark读取该测试数据,并通过groupByKey(12)对id分组处理,且Shuffle并行度为12。代码如下 publicclassSparkDataSkew{ publicstaticvoidmain(String[]args){ SparkSessionsparkSession=SparkSession.builder() .appName("SparkDataSkewTunning") .config("hive.metastore.uris","thrift://hadoop1:9083") .enableHiveSupport() .getOrCreate(); Dataset<Row>dataframe=sparkSession.sql("select*fromtest"); dataframe.toJavaRDD() .mapToPair((Rowrow)->newTuple2<Integer,String>(row.getInt(0),row.getString(1))) .groupByKey(12) .mapToPair((Tuple2<Integer,Iterable<String>>tuple)->{ intid=tuple._1(); AtomicIntegeratomicInteger=newAtomicInteger(0); tuple._2().forEach((Stringname)->atomicInteger.incrementAndGet()); returnnewTuple2<Integer,Integer>(id,atomicInteger.get()); }).count(); sparkSession.stop(); sparkSession.close(); } } 本次实验所使用集群节点数为4,每个节点可被Yarn使用的CPU核数为16,内存为16GB。使用如下方式提交上述应用,将启动4个Executor,每个Executor可使用核数为12(该配置并非生产环境下的最优配置,仅用于本文实验),可用内存为12GB。 spark-submit--queueambari--num-executors4 --executor-cores12 --executor-memory12g--classcom.jasongj.spark.driver.SparkDataSkew --masteryarn--deploy-modeclientSparkExample-with-dependencies-1.0.jar GroupBy Stage的Task状态如下图所示,Task 8处理的记录数为4500万,远大于(9倍于)其它11个Task处理的500万记录。而Task 8所耗费的时间为38秒,远高于其它11个Task的平均时间(16秒)。整个Stage的时间也为38秒,该时间主要由最慢的Task 8决定。 在这种情况下,可以通过调整Shuffle并行度,使得原来被分配到同一个Task(即该例中的Task 8)的不同Key分配到不同Task,从而降低Task 8所需处理的数据量,缓解数据倾斜。 通过groupByKey(48)将Shuffle并行度调整为48,重新提交到Spark。新的Job的GroupBy Stage所有Task状态如下图所示。 从上图可知,记录数最多的Task 20处理的记录数约为1125万,相比于并行度为12时Task 8的4500万,降低了75%左右,而其耗时从原来Task 8的38秒降到了24秒。 在这种场景下,调整并行度,并不意味着一定要增加并行度,也可能是减小并行度。如果通过groupByKey(11)将Shuffle并行度调整为11,重新提交到Spark。新Job的GroupBy Stage的所有Task状态如下图所示。 从上图可见,处理记录数最多的Task 6所处理的记录数约为1045万,耗时为23秒。处理记录数最少的Task 1处理的记录数约为545万,耗时12秒。 总结 适用场景 大量不同的Key被分配到了相同的Task造成该Task数据量过大。 解决方案 调整并行度。一般是增大并行度,但有时如本例减小并行度也可达到效果。 优势 实现简单,可在需要Shuffle的操作算子上直接设置并行度或者使用spark.default.parallelism设置。如果是Spark SQL,还可通过SET spark.sql.shuffle.partitions=[num_tasks]设置并行度。可用最小的代价解决问题。一般如果出现数据倾斜,都可以通过这种方法先试验几次,如果问题未解决,再尝试其它方法。 劣势 适用场景少,只能将分配到同一Task的不同Key分散开,但对于同一Key倾斜严重的情况该方法并不适用。并且该方法一般只能缓解数据倾斜,没有彻底消除问题。从实践经验来看,其效果一般。 自定义Partitioner 原理 使用自定义的Partitioner(默认为HashPartitioner),将原本被分配到同一个Task的不同Key分配到不同Task。 案例 以上述数据集为例,继续将并发度设置为12,但是在groupByKey算子上,使用自定义的Partitioner(实现如下) .groupByKey(newPartitioner(){ @Override publicintnumPartitions(){ return12; } @Override publicintgetPartition(Objectkey){ intid=Integer.parseInt(key.toString()); if(id>=9500000&&id<=9500084&&((id-9500000)%12)==0){ return(id-9500000)/12; }else{ returnid%12; } } }) 由下图可见,使用自定义Partition后,耗时最长的Task 6处理约1000万条数据,用时15秒。并且各Task所处理的数据集大小相当。 总结 适用场景 大量不同的Key被分配到了相同的Task造成该Task数据量过大。 解决方案 使用自定义的Partitioner实现类代替默认的HashPartitioner,尽量将所有不同的Key均匀分配到不同的Task中。 优势 不影响原有的并行度设计。如果改变并行度,后续Stage的并行度也会默认改变,可能会影响后续Stage。 劣势 适用场景有限,只能将不同Key分散开,对于同一Key对应数据集非常大的场景不适用。效果与调整并行度类似,只能缓解数据倾斜而不能完全消除数据倾斜。而且需要根据数据特点自定义专用的Partitioner,不够灵活。 将Reduce side Join转变为Map side Join 原理 通过Spark的Broadcast机制,将Reduce侧Join转化为Map侧Join,避免Shuffle从而完全消除Shuffle带来的数据倾斜。 案例 通过如下SQL创建一张具有倾斜Key且总记录数为1.5亿的大表test。 INSERTOVERWRITETABLEtest SELECTCAST(CASEWHENid<980000000THEN(95000000+(CAST(RAND()*4ASINT)+1)*48) ELSECAST(id/10ASINT)ENDASSTRING), name FROMstudent_external WHEREidBETWEEN900000000AND1050000000; 使用如下SQL创建一张数据分布均匀且总记录数为50万的小表test_new。 INSERTOVERWRITETABLEtest_new SELECTCAST(CAST(id/10ASINT)ASSTRING), name FROMstudent_delta_external WHEREidBETWEEN950000000AND950500000; 直接通过Spark Thrift Server提交如下SQL将表test与表test_new进行Join并将Join结果存于表test_join中。 INSERTOVERWRITETABLEtest_join SELECTtest_new.id,test_new.name FROMtest JOINtest_new ONtest.id=test_new.id; 该SQL对应的DAG如下图所示。从该图可见,该执行过程总共分为三个Stage,前两个用于从Hive中读取数据,同时二者进行Shuffle,通过最后一个Stage进行Join并将结果写入表test_join中。 从下图可见,最近Join Stage各Task处理的数据倾斜严重,处理数据量最大的Task耗时7.1分钟,远高于其它无数据倾斜的Task约2s秒的耗时。 接下来,尝试通过Broadcast实现Map侧Join。实现Map侧Join的方法,并非直接通过CACHE TABLE test_new将小表test_new进行cache。现通过如下SQL进行Join。 CACHE TABLE test_new; INSERT OVERWRITE TABLE test_join SELECT test_new.id, test_new.name FROM test JOIN test_new ON test.id = test_new.id; 通过如下DAG图可见,该操作仍分为三个Stage,且仍然有Shuffle存在,唯一不同的是,小表的读取不再直接扫描Hive表,而是扫描内存中缓存的表。 并且数据倾斜仍然存在。如下图所示,最慢的Task耗时为7.1分钟,远高于其它Task的约2秒。 正确的使用Broadcast实现Map侧Join的方式是,通过SET spark.sql.autoBroadcastJoinThreshold=104857600;将Broadcast的阈值设置得足够大。 再次通过如下SQL进行Join。 SET spark.sql.autoBroadcastJoinThreshold=104857600; INSERT OVERWRITE TABLE test_join SELECT test_new.id, test_new.name FROM test JOIN test_new ON test.id = test_new.id; 通过如下DAG图可见,该方案只包含一个Stage。 并且从下图可见,各Task耗时相当,无明显数据倾斜现象。并且总耗时为1.5分钟,远低于Reduce侧Join的7.3分钟。 总结 适用场景 参与Join的一边数据集足够小,可被加载进Driver并通过Broadcast方法广播到各个Executor中。 解决方案 在Java/Scala代码中将小数据集数据拉取到Driver,然后通过broadcast方案将小数据集的数据广播到各Executor。或者在使用SQL前,将broadcast的阈值调整得足够多,从而使用broadcast生效。进而将Reduce侧Join替换为Map侧Join。 优势 避免了Shuffle,彻底消除了数据倾斜产生的条件,可极大提升性能。 劣势 要求参与Join的一侧数据集足够小,并且主要适用于Join的场景,不适合聚合的场景,适用条件有限。 为skew的key增加随机前/后缀 原理 为数据量特别大的Key增加随机前/后缀,使得原来Key相同的数据变为Key不相同的数据,从而使倾斜的数据集分散到不同的Task中,彻底解决数据倾斜问题。Join另一则的数据中,与倾斜Key对应的部分数据,与随机前缀集作笛卡尔乘积,从而保证无论数据倾斜侧倾斜Key如何加前缀,都能与之正常Join。 案例 通过如下SQL,将id为9亿到9.08亿共800万条数据的id转为9500048或者9500096,其它数据的id除以100取整。从而该数据集中,id为9500048和9500096的数据各400万,其它id对应的数据记录数均为100条。这些数据存于名为test的表中。 对于另外一张小表test_new,取出50万条数据,并将id(递增且唯一)除以100取整,使得所有id都对应100条数据。 INSERT OVERWRITE TABLE test SELECT CAST(CASE WHEN id < 908000000 THEN (9500000 + (CAST (RAND() * 2 AS INT) + 1) * 48 ) ELSE CAST(id/100 AS INT) END AS STRING), name FROM student_external WHERE id BETWEEN 900000000 AND 1050000000; INSERT OVERWRITE TABLE test_new SELECT CAST(CAST(id/100 AS INT) AS STRING), name FROM student_delta_external WHERE id BETWEEN 950000000 AND 950500000; 通过如下代码,读取test表对应的文件夹内的数据并转换为JavaPairRDD存于leftRDD中,同样读取test表对应的数据存于rightRDD中。通过RDD的join算子对leftRDD与rightRDD进行Join,并指定并行度为48。 public class SparkDataSkew{ public static void main(String[] args) { SparkConf sparkConf = new SparkConf(); sparkConf.setAppName("DemoSparkDataFrameWithSkewedBigTableDirect"); sparkConf.set("spark.default.parallelism", parallelism + ""); JavaSparkContext javaSparkContext = new JavaSparkContext(sparkConf); JavaPairRDD<String, String> leftRDD = javaSparkContext.textFile("hdfs://hadoop1:8020/apps/hive/warehouse/default/test/") .mapToPair((String row) -> { String[] str = row.split(","); return new Tuple2<String, String>(str[0], str[1]); }); JavaPairRDD<String, String> rightRDD = javaSparkContext.textFile("hdfs://hadoop1:8020/apps/hive/warehouse/default/test_new/") .mapToPair((String row) -> { String[] str = row.split(","); return new Tuple2<String, String>(str[0], str[1]); }); leftRDD.join(rightRDD, parallelism) .mapToPair((Tuple2<String, Tuple2<String, String>> tuple) -> new Tuple2<String, String>(tuple._1(), tuple._2()._2())) .foreachPartition((Iterator<Tuple2<String, String>> iterator) -> { AtomicInteger atomicInteger = new AtomicInteger(); iterator.forEachRemaining((Tuple2<String, String> tuple) -> atomicInteger.incrementAndGet()); }); javaSparkContext.stop(); javaSparkContext.close(); } } 从下图可看出,整个Join耗时1分54秒,其中Join Stage耗时1.7分钟。 通过分析Join Stage的所有Task可知,在其它Task所处理记录数为192.71万的同时Task 32的处理的记录数为992.72万,故它耗时为1.7分钟,远高于其它Task的约10秒。这与上文准备数据集时,将id为9500048为9500096对应的数据量设置非常大,其它id对应的数据集非常均匀相符合。 现通过如下操作,实现倾斜Key的分散处理 将leftRDD中倾斜的key(即9500048与9500096)对应的数据单独过滤出来,且加上1到24的随机前缀,并将前缀与原数据用逗号分隔(以方便之后去掉前缀)形成单独的leftSkewRDD 将rightRDD中倾斜key对应的数据抽取出来,并通过flatMap操作将该数据集中每条数据均转换为24条数据(每条分别加上1到24的随机前缀),形成单独的rightSkewRDD 将leftSkewRDD与rightSkewRDD进行Join,并将并行度设置为48,且在Join过程中将随机前缀去掉,得到倾斜数据集的Join结果skewedJoinRDD 将leftRDD中不包含倾斜Key的数据抽取出来作为单独的leftUnSkewRDD 对leftUnSkewRDD与原始的rightRDD进行Join,并行度也设置为48,得到Join结果unskewedJoinRDD 通过union算子将skewedJoinRDD与unskewedJoinRDD进行合并,从而得到完整的Join结果集 具体实现代码如下 public class SparkDataSkew{ public static void main(String[] args) { int parallelism = 48; SparkConf sparkConf = new SparkConf(); sparkConf.setAppName("SolveDataSkewWithRandomPrefix"); sparkConf.set("spark.default.parallelism", parallelism + ""); JavaSparkContext javaSparkContext = new JavaSparkContext(sparkConf); JavaPairRDD<String, String> leftRDD = javaSparkContext.textFile("hdfs://hadoop1:8020/apps/hive/warehouse/default/test/") .mapToPair((String row) -> { String[] str = row.split(","); return new Tuple2<String, String>(str[0], str[1]); }); JavaPairRDD<String, String> rightRDD = javaSparkContext.textFile("hdfs://hadoop1:8020/apps/hive/warehouse/default/test_new/") .mapToPair((String row) -> { String[] str = row.split(","); return new Tuple2<String, String>(str[0], str[1]); }); String[] skewedKeyArray = new String[]{"9500048", "9500096"}; Set<String> skewedKeySet = new HashSet<String>(); List<String> addList = new ArrayList<String>(); for(int i = 1; i <=24; i++) { addList.add(i + ""); } for(String key : skewedKeyArray) { skewedKeySet.add(key); } Broadcast<Set<String>> skewedKeys = javaSparkContext.broadcast(skewedKeySet); Broadcast<List<String>> addListKeys = javaSparkContext.broadcast(addList); JavaPairRDD<String, String> leftSkewRDD = leftRDD .filter((Tuple2<String, String> tuple) -> skewedKeys.value().contains(tuple._1())) .mapToPair((Tuple2<String, String> tuple) -> new Tuple2<String, String>((new Random().nextInt(24) + 1) + "," + tuple._1(), tuple._2())); JavaPairRDD<String, String> rightSkewRDD = rightRDD.filter((Tuple2<String, String> tuple) -> skewedKeys.value().contains(tuple._1())) .flatMapToPair((Tuple2<String, String> tuple) -> addListKeys.value().stream() .map((String i) -> new Tuple2<String, String>( i + "," + tuple._1(), tuple._2())) .collect(Collectors.toList()) .iterator() ); JavaPairRDD<String, String> skewedJoinRDD = leftSkewRDD .join(rightSkewRDD, parallelism) .mapToPair((Tuple2<String, Tuple2<String, String>> tuple) -> new Tuple2<String, String>(tuple._1().split(",")[1], tuple._2()._2())); JavaPairRDD<String, String> leftUnSkewRDD = leftRDD.filter((Tuple2<String, String> tuple) -> !skewedKeys.value().contains(tuple._1())); JavaPairRDD<String, String> unskewedJoinRDD = leftUnSkewRDD.join(rightRDD, parallelism).mapToPair((Tuple2<String, Tuple2<String, String>> tuple) -> new Tuple2<String, String>(tuple._1(), tuple._2()._2())); skewedJoinRDD.union(unskewedJoinRDD).foreachPartition((Iterator<Tuple2<String, String>> iterator) -> { AtomicInteger atomicInteger = new AtomicInteger(); iterator.forEachRemaining((Tuple2<String, String> tuple) -> atomicInteger.incrementAndGet()); }); javaSparkContext.stop(); javaSparkContext.close(); } } 从下图可看出,整个Join耗时58秒,其中Join Stage耗时33秒。 通过分析Join Stage的所有Task可知 由于Join分倾斜数据集Join和非倾斜数据集Join,而各Join的并行度均为48,故总的并行度为96 由于提交任务时,设置的Executor个数为4,每个Executor的core数为12,故可用Core数为48,所以前48个Task同时启动(其Launch时间相同),后48个Task的启动时间各不相同(等待前面的Task结束才开始) 由于倾斜Key被加上随机前缀,原本相同的Key变为不同的Key,被分散到不同的Task处理,故在所有Task中,未发现所处理数据集明显高于其它Task的情况 实际上,由于倾斜Key与非倾斜Key的操作完全独立,可并行进行。而本实验受限于可用总核数为48,可同时运行的总Task数为48,故而该方案只是将总耗时减少一半(效率提升一倍)。如果资源充足,可并发执行Task数增多,该方案的优势将更为明显。在实际项目中,该方案往往可提升数倍至10倍的效率。 总结 适用场景 两张表都比较大,无法使用Map则Join。其中一个RDD有少数几个Key的数据量过大,另外一个RDD的Key分布较为均匀。 解决方案 将有数据倾斜的RDD中倾斜Key对应的数据集单独抽取出来加上随机前缀,另外一个RDD每条数据分别与随机前缀结合形成新的RDD(相当于将其数据增到到原来的N倍,N即为随机前缀的总个数),然后将二者Join并去掉前缀。然后将不包含倾斜Key的剩余数据进行Join。最后将两次Join的结果集通过union合并,即可得到全部Join结果。 优势 相对于Map则Join,更能适应大数据集的Join。如果资源充足,倾斜部分数据集与非倾斜部分数据集可并行进行,效率提升明显。且只针对倾斜部分的数据做数据扩展,增加的资源消耗有限。 劣势 如果倾斜Key非常多,则另一侧数据膨胀非常大,此方案不适用。而且此时对倾斜Key与非倾斜Key分开处理,需要扫描数据集两遍,增加了开销。 大表随机添加N种随机前缀,小表扩大N倍 原理 如果出现数据倾斜的Key比较多,上一种方法将这些大量的倾斜Key分拆出来,意义不大。此时更适合直接对存在数据倾斜的数据集全部加上随机前缀,然后对另外一个不存在严重数据倾斜的数据集整体与随机前缀集作笛卡尔乘积(即将数据量扩大N倍)。 案例 这里给出示例代码,读者可参考上文中分拆出少数倾斜Key添加随机前缀的方法,自行测试。 总结 适用场景 一个数据集存在的倾斜Key比较多,另外一个数据集数据分布比较均匀。 优势 对大部分场景都适用,效果不错。 劣势 需要将一个数据集整体扩大N倍,会增加资源消耗。 总结 对于数据倾斜,并无一个统一的一劳永逸的方法。更多的时候,是结合数据特点(数据集大小,倾斜Key的多少等)综合使用上文所述的多种方法。 作者:郭俊 Jason。来源Jason’s Blog,分享交流大数据领域技术,包括但不限于Storm、Spark、Hadoop等流行分布式计算系统,Kafka、MetaQ等分布式消息系统,MongoDB、Cassandra等NoSQL,PostgreSQL、MySQL等RDBMS及其它前沿技术。 本文作者:36大数据 来源:51CTO

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

Android应用性能优化最佳实践.2.6 合理的刷新机制

2.6 合理的刷新机制 在应用开发的过程中,因为数据的变化,需要刷新页面来展示新的数据,但频繁刷新会增加资源开销,并且可能导致卡顿发生,所以,需要一个合理的刷新机制来提高整体的UI流畅度。合理的刷新需要注意以下几点: 尽量减少刷新次数。 尽量避免后台有高CPU线程运行。 缩小刷新区域。 2.6.1 减少刷新次数 毫无疑问,减少刷新次数可以减少系统的开销,在功耗和页面的性能上可以表现得更优秀,但不刷新就不能及时让用户看到最新的数据,可以从以下几个方面减少刷新次数: 1.?控制刷新频率 在有些功能上需要频繁刷新某个控件(View),比如下载进度条或者播放进度条,没有必要在数据每次变化时都更新对应的控件,需要注意的是一定不能出现过度刷新(进度刷新频率大于系统显示的刷新频率)的情况。可以通过定时控制刷新频率,相同的刷新只做一次,比如播放进度条的刻度是10

资源下载

更多资源
Mario

Mario

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

腾讯云软件源

腾讯云软件源

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

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

WebStorm

WebStorm

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

用户登录
用户注册