文章目录
当某个job长时间运行没有结束,可能发生了数据倾斜。
设置map端聚合和二次group by保证reduce数据大概平均,然后再设置reduce数量减少每个reduce的数据量
尽量少用distinct,不仅吃不到map端聚合(distinct原理是全局排序去重),而且多个distinct也吃不到二次group的优化。
如果group by 多个字段,或者其它二次group失效的情况,可以走下方spark的解决方案,将hql分多个 hql来做。
比起hive来说,spark对数据倾斜的优化可以更细一些(也可以说更麻烦些)。
1.抽样检测分组字段,看看是否有倾斜
2.如果没有倾斜,就正常增加reduce数量,设置中间ORC+SNAPPY压缩
3.如果有倾斜,把倾斜的部分过滤出来加前缀打散处理,不倾斜的部分正常处理。
4.如果是大表join小表,大表倾斜,可以使用map端join方法,小表倾斜直接无视。
5.如果是大表join大表,某表倾斜,可以使用膨胀法处理。
跳表法快速排序
1 将值域划分区间建有序链表,原本海量的遍历会被压缩N倍。每个值域对应一个有序链表,因此是双重链表。
2 前提:除了跳表外还有一张作品表,以作品id为key,value含分数
3 值变更时,先查询作品表更新原值,通过原值到对应区间的链表中删除该值,新值插入对应区间的链表中。
4 求排名:1.每个区间链表除了保存作品和分数外,还额外维护一个区间总长
5 2.每次新值插入时,都将原区间总长-1,新区间总长+1
6 3.求排名时遍历第一层链表将区间总长累加,然后遍历该值所在的区间求出区间内排名,两者相加。
7
8 求TopN:第二层链表一路往下走N个。
9 缺点:事实上所有针对数据直接排序的方式都存在锁问题,每次更新只能有一个线程做,否则必然会乱。
平衡二叉树排序
1 假设总分为5 [0,5]
2 1.将总分一分为二 [0,2],[3,5]
3 2.将划分的部分继续二分 [0,1],[1,2],[3,4],[4,5]
4 3.重复2直到划分到底 [0,0],[1,1],[2,2],[3,3]...
5
6 采用链表的方式从顶点往下一路关联起来,每个区间key对应一个人数value。
7 假设有某个值从2变成3。
8 先从顶点开始往下遍历查找[2,2],遍历过程中经过的节点人数全部-1,终点[2,2]的人数也-1;
9 然后从顶点开始往下遍历查找[3,3],经过的节点人数全部+1。终点也+1。
10 求排名:假设要求分数为3的排名,从起点开始往下查找[3,3],如果是往左走,就把右值累加上,如果往右走则不加。
11 优点:毫秒级响应排名,更新不需要锁表,因为是单纯的加减日志。
12 缺点:消耗大量内存存储,每次更新都要改变N个值,每次更新值时还需要把排名添加到TopN表里。
理论:
如果是k-v形式的数据,或者是可以提取key的数据,可以使用分组法比较。
假设我有x G内存,要求A,B的交集。
先把A,B分别分组拆分成多个小文件,每个小文件的大小为(x-300)/2,超过的部分再水平拆分成多个 文件。
然后按分组读入A,B的数据,在内存中比较,求出交集部分。
如果分组太细,就按分组key的哈希进行分区,一次比较多个分组。
为了方便小文件的合并,可以在输出时将数据排序输出,创建索引文件,后续可以采用归并排序的方式 快速合并。
如果不能提取key,则按照hash分组。
实战:假设已经把两张表要求交集的部分字段拼接成"key"
select * from A join B on A.key = B.key
select * from A full outer join B on A.key = B.key where A.key is null or B.key null;
将字符串用哈希函数转换为一个或多个整型值,将bit型数组中对应位置上的0改为1。判断该字符串是否存在时,只需要判断这些位置上的值是否都为1,如果不是就说明一定不存在。但是反过来不能说明一定存在。
如:abc 转换为3和5,就将arr[3]和arr[5]的值设置为1,只要这两个值都为1,就说明abc可能存在
,如果它们不全为1,可以保证abc一定不存在。
TextFile
默认格式,按行存储,可以压缩但压缩文件不支持分片,反序列化开销是SequenceFile的几十倍(需要判断分隔符和换行符)
SequenceFile
hadoop原生支持,将kv以二进制方式按行存储,压缩后的文件支持压缩。默认以record压缩,可以改为block性能更好。压缩率很低,查询速度一般。
RCFile
按行分块、按列存储的存储方式,反序列化速度较慢,但压缩率和查询速度 快。
ORC file
RC的改良版,每个Task输出单文件、存储索引、支付复杂类型、支持块压缩、可以直接读取,ORC比RC高效很多。
Parquet
列式存储,是spark的默认存储格式,压缩和查询性能比ORC稍差,但是支持的编码更多,而且对嵌套式结构支持的更好(json)。
因此对结构化数仓来说ORC file格式更好,对灵活的spark作业来说Parquet格式更好。
上面虽然提到了压缩比,但只不过是相对于纯文本,在数据的存储方式上带来的数据量减少,并不是真正的压缩方式。
下方介绍在这些存储方式之上进一步减少数据量的压缩方式。
gzip
spark默认压缩方式,hadoop原生支持,压缩比率很高、压缩和解压速度很快,支持纯文本式编辑,使用方便,但是压缩后不支持分片,因此适用于1个块内大小的文件。
lzo
hadoop流行的压缩方式,需要安装,压缩解压速度较快,压缩率适中,建立索引后支持分片,压缩单个大文件时可以使用。
snappy
高速压缩和解压,压缩率较低,需要安装,不支持分片,推荐作为临时数据的压缩方式。
bzip2
非常高的压缩率,但解压速度很慢,支持分片。hadoop本身支持,但本地库不支持。一般和lzo选其中一个作为数据源的压缩格式。
速度上:改良的归并排序算法是n-nlogn,快速排序的时间复杂度是nlogn-n2,冒泡和选择都是n2。
稳定性:快速、选择都不稳定,冒泡和归并都稳定空间上:冒泡和选择都是1,快速为logn,归并为n
/**
* 一个完整的快速排序的方法
* 可以传入任意类型的buffer对其排序后返回
* 可以传入一个比较器对自定义类型排序 *
* @author 孤星魅影
* @param buffer 任意类型的buffer
* @param isASC 是否正序排序
* @param ev 自定义比较器
* @return 返回一个快速排序后的buffer */
def quickSort[T: ClassTag](buffer: mutable.Buffer[T])(implicit isASC: Boolean = true, ev: T => Comparable[T]): mutable.Buffer[T] = {
//为null、为空、长度为1时都返回buffer
if (buffer == null | | buffer.length <= 1)
return buffer
//1.将数组的第一个数head与后面所有的数进行比较,左边放比head小的数,右边放比head大的数 (从小到大)
//2.递归执行拆分,直到数组只剩下1个
//3.按顺序将左、中、右三者拼接起来,完成排序
val (left, right) = buffer.tail.partition(t => {
if (isASC) t.compareTo(buffer.head) < 0
else t.compareTo(buffer.head) > 0
})
quickSort(left) += buffer.head ++= quickSort(right)
}
scala
object{
lazy val obj = new T()
}
java
class T{
private static T obj = null;
private T(){}
//先执行一次null检验,然后上锁再执行一次null检验,防止同时有两个线程通过了第一次null检 验导致异常。
public T getObj()={
if(obj==null){
sysconized(obj){
if(obj == null) obj = new T();
}
}
return obj;
}
}
a,b,c,d = a*256*256*256+b*256*256+c*256+123
ip是256进制,所以a需要*3个256才能保证每个bcd不重复,b,c,d同理,使用位计算效率更高:*256 相当于<<8
提供一段scala代码:
def
ip2long(ip:String)=ip.split(".").reverse.zipWithIndex.map(v=>v._1<<(8*v._2)). reduce(_+_)
可以提取转换后的数字的前三位进行分组查询,效率更高:
ip.groupby(_.substring(0,3))
/**
* 求min - max以内的所有质数
* 只需要判断一个数是否能被自身以外的大于1的质数数整除就可以了
* @return */
def countPrime(min:Int,max:Int):Array[Int]={
val arr = mutable.Set[Int](1)
(min to max).foreach(index=> {
if(!arr.exists(prime=>{
//某个数如果能被除自己和1以外的质数整除,说明该数不是质数 if(prime>1 && index != prime && index % prime == 0) true
else false
}))
arr += index
})
arr.toArray.sorted
}
1 原理: 底层的左节点和右节点相差为1(左右子树相差为1),例子可以看上方海量数据实时排序部分 2 方法:从根节点开始,判断左子树与右子树是否差1,然后对左右子树不断递归这个判断直到没有子树。
package third;
import static java.lang.Thread.sleep;
public class SellShaoBing {
private int shaoBingAmount=0;
private boolean isStop = false;
private int maxAmount;
public SellShaoBing(int max){
maxAmount = max;
}
private Thread createProductThread(){
return new Thread(()->{
while (!isStop) {
synchronized (this) {
//留个缓冲,做满之前就开始卖
if (shaoBingAmount > maxAmount - 5) notifyAll(); waitMethod(shaoBingAmount == maxAmount); sleepMethod();
System.out.println("做烧饼");
shaoBingAmount++;
System.out.println("当前烧饼数量"+shaoBingAmount); }
}
});
}
private Thread createConsumerThread(){
return new Thread(()->{
while (!isStop) {
synchronized (this) {
//留个缓冲,烧饼卖完之间就通知生产 if (shaoBingAmount < maxAmount /4) notifyAll(); waitMethod(shaoBingAmount == 0); sleepMethod();
System.out.println("卖烧饼");
shaoBingAmount--;
System.out.println("当前烧饼数量"+shaoBingAmount); }
}
});
}
public void actions(){
Thread product = createProductThread();
Thread consumer = createConsumerThread();
consumer.start();
product.start();
try {
sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
isStop = true;
}
private synchronized void waitMethod(boolean iswait){ if (iswait) {
try {
wait();
} catch (InterruptedException e) { e.printStackTrace();
Thread.currentThread().interrupt(); }
}
}
private void sleepMethod(){
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt(); }
}
}
package utils
//卖烧饼 (Java生产者消费者的Scala实现)
case class ShaoBing(var
amount:Int=0,maxAmount:Int=30,isContinue:Boolean=true)
object ShaoBing{
/**
* 烧饼线程模板
* @param isNotify 传入一个唤醒所有线程的判断方法
* @param isWait 传入一个使当前线程等待的方法
* @param doMethod 传入一个增减烧饼数量的方法
* @param shaoBing 传入一个烧饼对象 */
def createThread(isNotify:ShaoBing => Boolean , isWait:ShaoBing => Boolean , doMethod:ShaoBing => Unit)(shaoBing:ShaoBing)={
while (shaoBing.isContinue){
//notify和wait都需要在同步代码块中才能使用,java 的 synchronized(obj) {...} = scala 的 obj.synchronized{...}
shaoBing.synchronized{
if (isNotify(shaoBing)) shaoBing.notifyAll()
//传入等待条件,使线程等待
if (isWait(shaoBing)) shaoBing.wait()
//增加或减少烧饼数量时才上锁
doMethod(shaoBing)
}
println("当前烧饼数量:"+shaoBing.amount)
Thread.sleep(1000)
}
}
//做烧饼
val createProducerThread = createThread(
//做满之前就开始卖
isNotify = shaoBing=>shaoBing.amount > shaoBing.maxAmount-5, //做满之后就等
isWait = shaoBing=>shaoBing.amount == shaoBing.maxAmount, _.amount += 1) _
//卖烧饼
val createConsumerThread = createThread(
//卖到剩四分之一时候提前开始生产烧饼
isNotify = shaoBing=>shaoBing.amount < shaoBing.maxAmount/4, //卖光了就停止卖烧饼
isWait = shaoBing=>shaoBing.amount == 0,
_.amount -= 1) _
//启动生产者和消费者
def main(args:Array[String]):Unit={
val shaoBing = ShaoBing()
val producer = new Thread(new Runnable {override def run(): Unit = createProducerThread(shaoBing)})
val consumer = new Thread(new Runnable {override def run(): Unit = createConsumerThread(shaoBing)})
producer.start()
consumer.start()
}
}
IO共有四种模型:同步阻塞、同步非阻塞、异步阻塞、异步非阻塞同步阻塞:系统内核做好读写数据的准备之前,用户线程一直等待。
同步非阻塞:用户线程使用一个循环不断询问内核是否准备就绪,在准备就绪以前会一直进行该循环。
异步阻塞:用户线程启动一个新的线程去同步阻塞,自己则做下一件事,新的线程不断询问系统内核并阻塞。
异步非阻塞:当用户线程收到通知时,数据已经被内核读取完毕,并放在了用户线程指定的缓冲区内,内核在IO完成后通知用户线程直接使用即可
Java后端
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳
我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01 客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02 数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit
文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co
我正在尝试在Rails上安装ruby,到目前为止一切都已安装,但是当我尝试使用rakedb:create创建数据库时,我收到一个奇怪的错误:dyld:lazysymbolbindingfailed:Symbolnotfound:_mysql_get_client_infoReferencedfrom:/Library/Ruby/Gems/1.8/gems/mysql2-0.3.11/lib/mysql2/mysql2.bundleExpectedin:flatnamespacedyld:Symbolnotfound:_mysql_get_client_infoReferencedf
文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,
SPI接收数据左移一位问题目录SPI接收数据左移一位问题一、问题描述二、问题分析三、探究原理四、经验总结最近在工作在学习调试SPI的过程中遇到一个问题——接收数据整体向左移了一位(1bit)。SPI数据收发是数据交换,因此接收数据时从第二个字节开始才是有效数据,也就是数据整体向右移一个字节(1byte)。请教前辈之后也没有得到解决,通过在网上查阅前人经验终于解决问题,所以写一个避坑经验总结。实际背景:MCU与一款芯片使用spi通信,MCU作为主机,芯片作为从机。这款芯片采用的是它规定的六线SPI,多了两根线:RDY和INT,这样从机就可以主动请求主机给主机发送数据了。一、问题描述根据从机芯片手
前言一般来说,前端根据后台返回code码展示对应内容只需要在前台判断code值展示对应的内容即可,但要是匹配的code码比较多或者多个页面用到时,为了便于后期维护,后台就会使用字典表让前端匹配,下面我将在微信小程序中通过wxs的方法实现这个操作。为什么要使用wxs?{{method(a,b)}}可以看到,上述代码是一个调用方法传值的操作,在vue中很常见,多用于数据之间的转换,但由于微信小程序诸多限制的原因,你并不能优雅的这样操作,可能有人会说,为什么不用if判断实现呢?但是if判断的局限性在于如果存在数据量过大时,大量重复性操作和if判断会让你的代码显得异常冗余。wxswxs相当于是一个独立