大数据架构师之路

1、同时拥有实时和离线处理的架构,既保证低延迟,又保障正确性。这个方法被称作 Lambda 架构,它通过批量 MapReduce作业提供了虽有些延迟但是结果准确的计算,同时通过flink/Storm将最新数据的计算结果初步展示出来。
双路生产会存在一些问题,比如加工逻辑double,开发运维也会double,资源同样会变成两个资源链路。因为存在以上问题,所以又演进了一个Kappa架构。
Kappa架构从架构设计来讲比较简单,生产统一,一套逻辑同时生产离线和实时。但是在实际应用场景有比较大的局限性,在业内直接用Kappa架构生产落地的案例不多见,且场景比较单一
2、Doris也是一种OLAP框架,主要用于实时数据仓库,自带存储的实时计算引擎。
MPP架构的SQL查询引擎,如Impala,presto等能够高效地支持SQL查询,但是仍然需要依赖Kudu, HDFS, Hive Metastore等组件, 运维成本依然比较高,同时,由于计算存储分离,查询引擎不能很好地及时感知存储层的数据变化,就无法做更细致的查询优化,如想在SQL层做缓存就无法保证查询的结果是最新的。因此,我们的目标是寻求一款【计算存储一体】的MPP数据库来替代我们目前的存储计算层的组件。
Doris是百度开源到Apache社区的基于 MPP 的交互式 SQL 数据仓库, 主要用于解决报表和多维分析,像flink,storm就是只有计算没有存储的。
3、大数据架构分为数据源层、数据加工层、数据服务层、数据应用层。

数据源层:包含接入的原始数据,包括客户端日志、服务端日志、业务库、集团数据、外部数据等。
数据加工层:使用Spark、Hive 构建离线数仓、使用Storm、 Flink实时数仓。在数仓之上针对服务对象建设各种数据集市
数据服务层:主要包括存储介质的使用和数据服务的方式。存储:主要使用开源组件,如 Mysql, HDFS, HBase, Kylin, Doris, Druid, ES等。数据服务:对外数据查询、接口以及报表服务
数据应用层:主要包括主题报表、自助取数工具、增值产品、数据分析等支撑业务开展,同时依赖公司平台提供的一些工具建设整体数据应用。

4、MPP是一种实时海量数据分析架构。MapReduce是一种离线海量数据分析架构。其实MPP架构的关系型数据库与Hadoop的理论基础是很相似的,都是将运算分布到节点中独立运算后进行结果合并。只不过MPP底层跑的是SQL,而Hadoop底层执行的是MR。
ES也是一种MPP架构的数据库,Presto、Impala等都是MPP engine,各节点不共享资源,每个executor可以独自完成数据的读取和计算,缺点在于怕stragglers,即所谓木桶的短板。与hadoop相比,MPP更加强调的实时计算。
5、MPP架构和批处理架构(MR,SPARK)区别:mpp架构每次计算是所有节点都参与计算。批处理架构并不需要所有的节点都参与运算,它在一个任务事件下发以后,控制节点会分配给一些集群中的节点,而这些节点各自完成自己的计算,然后把计算结果写到磁盘里,再交给下一个计算的节点去写入,每次不需要所有的节点去参与运算。
批处理架构需要节点和任务去进行解耦,解耦的代价是,需要共享资源,势必会带来写磁盘,不管是读磁盘还是写磁盘,相比MPP的通信方式来说显然会更慢。
可以将两者进行互补:MPP on Hadoop就不得不提一下,如Impala,presto,在这里就把他们归类为MPP on Hadoop技术。这些技术大部分没有自己的存储,是一个类MPP的架构,需要控制节点把任务下发到对应的MPP的任务节点上,而在MPP节点的底层是HDFS,等于是这两者的一个结合,实际运用起来查询会比Hive更快一些。
总结:MPP会将任务下发到每个节点,每个节点完成所有计算。而MR/spark是将任务下发到某些节点,由于资源共享涉及shuffle,所以较慢
6、市面上的OLAP分为两种:
通过加并发的方式来解决问题:MPP架构和批处理架构
通过预计算来解决问题,如麒麟,druid
10、分类学习之召回率和准确率
假设我们手上有60个正样本,40个负样本,我们要找出所有的正样本,系统查找出50个,其中只有40个是真正的正样本,计算上述各指标。
TP: 将正类预测为正类数 40
FN: 将正类预测为负类数 20
FP: 将负类预测为正类数 10
TN: 将负类预测为负类数 30
精确率(precision) = 40/50 = 80% 精确率是针对我们预测结果而言的,它表示的是预测为正的样本中有多少是真正的正样本
召回率(recall) = 40/60 = 2/3 召回率是针对我们原来的样本而言的,它表示的是样本中的正例有多少被预测正确了
预测正确了就称为召回

14、G1回收器的缺点:需要用记忆集(卡表)来记录新生代和老年代之间的引用关系。这种数据结构需要占用大量的内存,带来高负载
15、对于文件存储而言,有两种主流的方式,即按行存储以及按列存储。所谓按行存储就是把每一行数据依次存储在一起,即先存储第一行的数据再存储第二行的数据,以此类推。按列存储就是把表中的数据按照列存储在一起,先存储第一列的数据,再存储第二列的数据。而在大数据场景之下,往往只需要获取部分列的数据,那么使用列存就可以只读取少量数据,这样可以节省大量磁盘和网络 I/O 的消耗。此外,因为相同列的数据属性非常相似,冗余度非常高,列式存储可以增大数据压缩率,进而大大节省磁盘空间
16、orc优化:https://www.infoq.cn/article/spRaKpgIGyNQAdUwmRiT

  • 异步预读:传统读文件的方式一般是从底层文件系统先拿到原始数据,然后进行解压和解码。这两步操作分别是 I/O 密集型和 CPU密集型的任务,并且两者没有任何并行性,因此就加长了整体的端到端时间。AliORC这样就将所有的读盘操作变成了异步的操作,实现了从文件系统读数据和解压解码操作的并行处理
  • 消除小IO:在 ORC 文件中,而每次读取都是以列为单位进行的。这样对于数据量比较小的列而言,读取时的网络 I/O 开销非常大。而 ORC文件中有许多这样数据量很小的列,从而造成了大量小 I/O 的产生。为了消除这些小 I/O 开销,AliORC 在 Writer写数据时,针对不同列的数据压缩后大小进行了排序,将数据量少的列放在一起写
  • 内存管理:在开源版本的 ORC 实现中,Writer 的每列数据都使用了一个很大的 Buffer 去保存压缩后的数据,默认大小为1M。Buffer 设置得越大,压缩率越高。但是不同列的数据量不同,某些列根本用不到 1M 大小的Buffer,因此就会造成极大的内存浪费。避免内存浪费的简单方法就是在一开始的时候只给很小的数据块作为 Buffer,并且按需分配

17、orc和parquet对比:相同压缩算法下,Parquet 和 ORC 存储性能非常相近。orc压缩率更高点。
关于读表性能的对比,相同压缩算法的 ORC 文件读起来比 Parquet 要更快一些。
嵌套结构支持:Parquet 能够很完美的支持嵌套式结构,而在这一点上 ORC 支持的并不好,表达起来复杂且性能和空间都损耗较大。比如某个字段的数据嵌套了多层,那parquet可以很完美的存储这样的字段,orc存储起来就比较吃力
18、ORC基于数据类型的块模式压缩:

  • integer类型的列用行程长度编码(run-length encoding);
  • String类型的列用字典编码(dictionary encoding);

19、ORC将整个表数据先划分为多个strip,每个strip包含多行数据。在strip内部是列式存储。但是从整个表的存储角度,orc并不是完整的列式存储。即orc并不是纯粹的列式存储,也是先基于行对数据表进行分组(行组),然后对行组进行列式存储。
20、Hive的ORC文件格式,它不但有着很高的压缩比,节省存储和计算资源之外,还通过一个内置的轻量级索引,提升查询的性能。这个内置的轻量级索引,就是下面所说的Row Group Index。
其实ORC支持的索引不止这一种,还有一种BloomFilter索引,两者结合起来,更加提升了Hive中基于ORC的查询性能。
在建立ORC格式表时,指定表参数’orc.create.index’=’true’之后,便会建立Row Group Index,需要注意的是,为了使Row Group Index有效利用,向表中加载数据时,必须对需要使用索引的字段进行排序,否则,min/max会失去意义。另外,这种索引通常用于数值型字段的查询过滤优化上。

21、hadoop archive 存档过程实际是一个MapReduce过程
/* 归档命令:
hadoop archive -archiveName 0825.har -p /test/in/ small mapjoin /test/in/har

  • -archiveName 0825.har : 指定归档后的文件名
  • -p /test/in/ : 被归档文件所在的父目录。这里也可以写多个需要归档的文件
  • small mapjoin : 要被归档的目录,一至多个(small和mapjoin)
  • /test/in/har : 生成的归档文件存储目录
    */

归档可以解决namenode内存占用问题。但无法解决小文件造成的spark或者MR输入多个map的问题。因为它仍然允许对归档文件中小文件进行透明的访问。归档有如下问题:
存档文件的源文件及目录都不会自动删除,需要手动删除
使用 HAR 作为MR的输入,MR可以访问其中所有的文件。但是由于InputFormat不会意识到这是个归档文件,也就不会有意识的将多个文件划分到单独的Input-Split中,所以依然是按照多个小文件来进行处理,效率依然不高

22、YARN 是一个资源管理系统。主要由 ResourceManager、NodeManager、ApplicationMaster 和 Container 等组件构成。而hadoop的架构是namenode,datanode,Seconddary NameNode。运行的hadoop任务,需要向yarn申请资源。所以不要把NodeManager和DataNode等搞混了
(1)NameNode与ResourceManager分开部署(都是老大)
(2)datanode 与NodeManager ,一般保存在同一个节点上。DataNode与NodeManager部署在一起是为了可以就近拿数据,而不是通过网络去别的节点上取数据。
23、mr为什么要进行排序?
mapper 对每段数据先做排序,reducer的shuffle 对排好序的每段数据做归并。也就是map先对每段数据排序,排好序的数据会比较好处理。下游处理起来会很快。比如reduce做归并时,对map输出的一段一段的有序数据做归并排序时会很快。所以map输出的有序数据能极大缓解下游reduce处理时的压力。
在Map任务和Reduce任务的过程中,一共发生了3次排序
1)当map函数产生输出时,会首先写入内存的环形缓冲区,当达到设定的阀值,在刷写磁盘之前,后台线程会将缓冲区的数据划分成相应的分区。在每个分区中,后台线程按键(key)进行内排序(内存排序)
2)在Map任务完成之前,磁盘上存在多个已经分好区,并排好序的,大小和缓冲区一样的溢写文件,这时溢写文件将被合并成一个已分区(每个分区有序,将所有分区数据合并,很适合用归并排序,将各分区有序数据合并成一个全序数据)且已排序的输出文件。由于溢写文件已经经过第一次排序,所以合并文件只需要再做一次排序即可使输出文件整体有序。合并文件,归并排序。
3)在reduce阶段,需要将多个Map任务的输出文件copy到ReduceTask中后合并,由于经过第二次排序,所以合并文件时只需再做一次排序即可使输出文件整体有序。合并文件,归并排序。
在这3次排序中第一次是内存缓冲区做的内排序,使用的算法使快速排序,第二次排序和第三次排序都是在文件合并阶段发生的,使用的是归并排序。
mr的输出数据是有序的,为许多应用和后续应用开发带来很多好处。虽然也有并不关心数据是否局部有序的应用场景,但在数据访问和计算中,保证数据有序性是一个必要功能。反正就是一个排好序的数据,后面不管是做合并还是其他开发,都会好处理的多
24、MR中map数据在写入磁盘之前,线程首先根据reduce任务的数目将数据划分为相同数目的分区,也就是一个reduce任务对应一个分区的数据
25、flume hdfs sink产生小文件:# 将这几个字段的值设置为0可减少小文件
hdfs.rollInterval 30 每隔30秒截断一个文件。设置为0表示不会因为时间间隔截断文件
hdfs.rollSize 1024 文件字节数超过1024截断一个文件。设置为0就不因为文件大小截断文件
hdfs.rollCount 10 每10个event截断一个文件。设置为0就不因为event数量截断文件
26、kafka零拷贝:指不需要将文件内容拷贝到用户空间(User Space)。而直接在内核空间(Kernel Space)中传输到网络的方式,减少了内核和用户模式之间的上下文切换。零拷贝并不是不需要拷贝,而是减少不必要的拷贝次数。通常是说在 IO 读写过程中。实现零拷贝的API有: Memory Mapped Files 和 sendfile。
传统方式,读取磁盘文件并进行网络发送需要多次copy:

  • 1、第一次:将磁盘文件,读取到内核态的read buffer;
  • 2、第二次:将read buffer的数据,copy到用户态的application buffer;
  • 3、第三步:将application buffer的数据,copy到内核态的socket buffer
  • 4、第四次:将socket buffer的数据,copy到网卡,由网卡进行网络传输。

sendfile 是将读到内核空间的数据,转到socket buffer,进行网络发送;mmap将磁盘文件映射到内存,支持读和写,对内存的操作会反映在磁盘文件上。数据直接在内核完成输入和输出,不需要拷贝到用户空间再写出去。也就是省略了上面的2,3步。
kafka里面对零拷贝的引用:

  • Producer生产的数据持久化到broker,采用mmap文件映射,实现顺序的快速写入;
  • Customer从broker读取数据,采用sendfile,将磁盘文件读到OS内核缓冲区后,直接转到socket
    buffer进行网络发送。

28、spark streaming写hdfs小文件
使用 Spark Streaming 时,如果实时计算结果要写入到 HDFS,那么不可避免的会遇到一个问题,那就是在默认情况下会产生非常多的小文件,这是由 Spark Streaming 的微批处理模式和 DStream(RDD) 的分布式(partition)特性导致的,Spark Streaming 为每个 Partition 启动一个独立的线程(一个 task/partition 一个线程)来处理数据,一旦文件输出到 HDFS,那么这个文件流就关闭了,再来一个 batch 的 parttition 任务,就再使用一个新的文件流。
解决:1、增加 batch 大小 2、重分区:Coalesce大法好
29、sdk埋点:将sdk代码集成到APP程序里面,当我们使用app的时候,就可以将一些操作行为数据记录下来,发送到sdk服务器,采集下来
30、一致性hash和hash槽
一致性hash

  • 1)先声明一个环空间,顺时针方向:环上数据依次增大0,1,2,3,4…2^32-1
  • 2)将服务器节点分配到环上:将服务器的ip或主机名作为关键字进行哈希取值,映射到圆环上
  • 3)将数据分配到节点上:将数据key进行哈希取值,映射到数组圆环上,从映射位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器。
  • 4)当在环上增加节点时,新增节点映射到的环上的点,周围一小部分数据需要重新分配

hash槽

  • 1)先声明一个槽空间:一共有16384个槽,每个槽可以理解为一个分区。集群使用公式 CRC16(key) % 16384来计算redis的key属于哪个槽。CRC16算法计算出来的也是一种hash值。
    当前集群有3个节点,槽默认是平均分的:
    1、节点 A (6381)包含 0 到 5499号哈希槽.
    2、节点 B (6382)包含5500 到 10999 号哈希槽.
    3、节点 C (6383)包含11000 到 16383号哈希槽.
    这种结构很容易添加或者删除节点.。比如如果我想新添加个节点D, 我需要从节点 A, B, C中的部分槽到D上。如果我想移除节点A,需要将A中得槽移到B和C节点上。然后将没有任何槽的A节点从集群中移除即可.。由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态。
    当新增或删除master节点时,需要对槽进行重分配,而不是直接对数据进行分配。对于hash槽的转移和分配,redis不会自动进行,需要人工辅助,即通过人工输入命令重新分配。

  • 2)当增加节点时,只需要对槽重新分配,不需要直接对数据分配。槽的量相对来说是比较少的。所以重分配成本较低

31、hdfs存储冷备:将数据存储到廉价的机器或者介质上,比如阿里云oss。阿里云oss存储相比自建机房,会便宜很多
32、生命周期管理也是存储优化的重点
33、副本策略和纠删码是存储领域常见的两种数据冗余(容错)技术
多副本策略即将数据存储多个副本(一般是三副本,比如HDFS),当某个副本丢失时,可以通过其他副本复制回来。三副本的磁盘利用率为1/3。
纠删码技术主要是通过纠删码算法将原始的数据进行编码得到冗余,并将数据和冗余一并存储起来,以达到容错的目的。当数据丢失时,可通过重构算法将冗余恢复成相应的数据
34、hdfs冷数据集群:搭建一个新的hadoop 3.x集群,集群不用三副本策略,而用的是EC码存储。
hdfs3和hdfs2.x相比,增加了基于纠删码(erasure encoding)的容错方式。也就是安装好hadoop 3集群后,hadoop就提供了ec相关命令及api,调用这些api就可以写入ec存储的数据

在 Hive 新表上开启 EC:
hdfs ec -setPolicy -policy RS-3-2-1024k -path <table_location>
EC默认使用的策略为 RS-6-3-1024k,即文件被分割成 6 个数据单元(data cells)和 3 个奇偶校验单元(parity cells),总共占用 9 个磁盘数据块。
RS - 编解码器 指定用于编码和解码的编解码器。RS 代表 Reed Solomon。这是目前唯一支持的编解码器类型。
6 - 数据单元(data cells)的数量 设置数据被分割成多少块(数据单元)。在本例中,每个 stripe 中有 6 个数据单元格。
3 - 奇偶校验单元(parity cells)的数量 设置计算和存储多少奇偶校验单元。奇偶校验单元用于数据恢复。当数据丢失或损坏时,奇偶校验单元用于解码和重构丢失的数据。在本例中,每个条带中有三个奇偶校验单元。
1024k - 每个单元格的大小

35、广告名词

  • 品牌广告:追求长远的品牌形象塑造,追求占领用户心智
  • 效果广告:追求即可转化,追求让消费者所见即所购;比如社交广告,信息流广告,电商广告等,“99元9件”
  • 代理商:广告主的业务代理人,负责对接广告主需求,并代表广告主寻找媒体渠道进行合作。
  • DSP:Demand-Side Platform,为需求方(广告主或代理商)提供实时竞价投放的平台。
  • TD:Trading Desk 需求方可以在TD上统一管理多个DSP平台的投放,包括分配投放预算、制定和调整投放策略、查看数据报告等
  • SSP:Supply-Side Platform 供应方平台 对接媒体和Ad Exchange,目前SSP的功能和Ad
    Exchange一致了,因此把Ad Exchange和SSP一起,统称为广告交易平台。 AdX:Ad Exchange
  • 其实DSP就是买方平台,想买东西的人就上这来。SSP就是卖方平台,能提供广告位的人就上这来,比如浏览器有广告位出售,就挂到SSP的摊位上统一管理。SSP上有多家广告商提供的广告位
  • CPM:Cost Per Mille/Cost Per Thousand Impressions 广告被展示1000次对应的价格。CPM=(成本/总展示量) * 1000
  • eCPM :Effective Cost Per Mille,有效千人成本 是指从1000次广告展示量中实际产生的广告收入。其公式是:eCPM = (收入 / 总展示量) * 1000
  • CPM是对广告主说的词,你要花多少钱,买一千次广告展示机会;eCPM是对媒体说的词,你每展示一千次广告,能赚多少钱
  • CTR :Click Through Rate 广告点击率 广告点击次数占广告展示次数的百分比。CTR =
    (Click/Impression) * 100%
  • pCTR:predicted CTR 预估点击率
  • 自然下载=总下载-CPD下载 自然下载就是没有产生计费的下载,跟产生计费的下载CPD相对

36、dqc:数据质量中心,对数据仓库表的数据变化进行监控。根据用户设定采集项配置、规则项配置、预警规则设置(枚举值),对用户指定的表进行每日定时数据采集、计算,并与历史数据或维表进行比对验证。最终将触发预警规则的异常数据以短信、邮件、App 等方式及时通知给用户。并处理问题,优化任务,形成闭环
37、分而治之和动态规划很像,都是将大任务拆分成许多小任务。但是分治的小任务之间是相互独立的。动态规划的小任务之间是有联系的,后面的任务依赖于前面的任务
38、hadoop最后一个reduce个数就是输出文件数。如果没有reduce就是最后一个map数
39、hadoop从读文件,到map输出,到最后输出都涉及压缩,各环节的压缩算法都不一样。比如最后输出的肯定是压缩比最高的,而中间map输出可以用压缩速度快的
40、大数据不会用三范式,因为这样会产生许多小表,需要很多join,大数据里面效率极低
41、hive不是数据仓库。只是实现数仓的一种技术
42、幂等性能保证单分区单会话不重复,加事务可以保证全局不重复,但是会影响效率因为每条数据进来都会判断一下。所以大部分情况下不用,数据重就重了问题不大
43、kafka producer端开启压缩,只需要在配置中添加一项:
kafkaProperties.setProperty(“compression.type”, “gzip”) // 开启压缩
为什么不在集群层面统一开启压缩?
因为broker端与producer端指定的压缩方式可能不一致。举个例子,producer端指定了压缩算法为gzip,broker端指定了压缩算法为snappy,在这种情况下broker接收到gzip压缩的消息后,只能先解压缩然后使用snappy重新压缩一遍,无疑增大了开销。
而且batch size过小的情况下开启压缩,反而事倍功半。所以只有batch.size设置得较大的时候,压缩的效果才会更好
kafkaProperties.setProperty(“batch.size”, “10240”) // 每个批次大小的上限
kafkaProperties.setProperty(“linger.ms”, “5”) // 批次等待发送的最大时长
kafkaProperties.setProperty(“compression.type”, “gzip”) // 开启压缩
44、Eden的内存太小会频繁的进行minor gc,导致有些短生命周期对象没有被回收掉(比如正常10s进行一次minorGC,然后短生命周期对象存活时间是7s,正常a会在被minorGC回收,而频繁GC的话,如1s一次,a可能就不会被minorGC回收,进而被放到老年代了),年龄变大放到老年代了,导致full gc。所以如果进行了多次 minorGC,分配更多的内存给Eden也许会有帮助
46、现有的标签数据几乎都是基于规则类的标签,偏好挖掘类的标签较少。在做用户偏好标签的时候,实现需要确定标签体系,如将用户的偏好标签分为几十个标签,比如:科幻、搞笑、历史 … 等。确定好偏好标签体系之后,该问题就转化成一个文本分类问题。常用的文本分类算法主要有:朴素贝叶斯、支持向量机、卷积神经网络、循环神经网络、FastText等。
47、FastText是一种文本分类器,具有预测精度高且效率快的特点。
基于FastText的用户偏好预测技术架构分为三层:特征层、模型层及应用层;
(1)特征层:将文本分词、去停用词、清除特殊符号、根据TFIDF模型去掉影响力较低的词,提取文本的N-gram信息。去掉特殊字符以及繁体字。采用词语分词和单字分词两种数据去训练模型,结果发现分词去掉影响力较低的词语,然后再将分词后的结果拆成单字作为特征训练模型的效果比较好
(2)模型层:模型层的主要作用是利用特征层的特征(处理好后,分好词后的文本)训练模型,然后使用测试样本评估模型的优劣,当模型达到最优的时候持久化模型到存储介质,然后利用该模型去预测待预测的样本。
(3)应用层:数据经过模型层后,是已经打上标签的文本数据和文本对应的ID,如下:
0009 基 多 拉 竟 然 成 工 具 人 {“label_篮球”: 0.06, “label_电影”: 0.05, “label_音乐”: 0.01, …}
在应用层根据用户对文本的点击次数、曝光次数、阅读时长等行为加权得到用户最关心的top3的文本数据,此时文本数据已经打上标签。对文本数据分类标签分数求和,然后做sigmoid归一化操作,最终产出用户对应每个标签的分值。
48、利用hive数据做机器学习的过程:ods-dw-dm,然后利用pyspark读取dm或者dw表文本数据,调用模型对数据预测,最后将预测结果(标签)写到hive,最后下游服务就去拿这个标签使用
50、hive数据同步到redis,es方案

  • 1)hive jdbc,可以用java代码实现复杂逻辑
  • 2)spark读取hive写到redis,或者es

51、mongo联合索引:如果给A,B两个字段建立了联合索引(A在前面),如果查询的时候A,B两个字段都用上了查询,那么会走索引。如果只查询A字段,那么也会走索引。如果只查询B字段,那么不会走索引,那就会很慢。
如果查询慢,就用explain看查询计划,可以看到查询是否走索引,“stage”: “IXSCAN” 表示走索引扫描, “stage” : "COLLSCAN"表示全表扫描。
spark读取mongo,貌似不会走索引。所以还是不要用spark去读mongo了。用spark读取mongo,不会走索引。所以对于索引字段的查询,还是 用Python或者java单机去跑就很快了
52、查看剩余磁盘空间:df -h
53、redis集群模式有多个主节点A,B,C,用集群模式连接A,写入某条数据的时候,这条数据只会写到某个主节点(不一定是连接的那台主节点)
54、加密技术可以分为对称与非对称两种
对称加密,即加密与解密用的是同一把秘钥,常用的对称加密技术有DES,AES等。而非对称技术,加密与解密用的是不同的秘钥(私钥和公钥都称为秘钥),常用的非对称加密技术有RSA等。
RSA加解密会同时生成一个公钥和一个私钥,一般都是用公钥加密,私钥解密。RSA加解密速度慢,不适合大量数据文件加密;
AES加密方和解密方适用同一个秘钥。密解密的速度比较快,适合数据比较长时使用。
AES+RSA:使用AES对称密码对传输数据加密,同时使用RSA不对称密码来传送AES的密钥。比如请求接口的时候,对请求参数进行AES加密(传入AES秘钥,偏移量,请求参数(加密的内容)),对请求参数解密的时候,需要用到AES秘钥,所以除了传请求参数过去,还要单独把AES秘钥传过去。但是AES秘钥不能明文传输,所以需要用RSA对AES秘钥进行加密(传入RSA秘钥,AES秘钥(加密的内容))
55、base64在加密过程中很重要,比如得到加密的byte[]类型的数据后,还要对这个byte类型进行base64加密得到base64加密字符串,传输的时候就传输这个base64加密后的字符串
56、Nginx的日志格式是固定的
1、从web ui日志查看倾斜的是哪一段sql:一般一个复杂sql都会涉及多个表,比如map端日志出现“reading from 某个表路径”,就可以定位到倾斜在哪个表上,也就是读这个表的分组操作或者count distinct等操作倾斜了
2、group by通过随机数优化
原sql: select id,age,count(0) from test group by id, age
假设age这个字段倾斜了,则对age这个字段加随机数:
第一步:加随机数group by,相当于预聚合
select id, concat(age, “", floor(1000+rand()*8999)) as age_tmp, count(0) as cnt as age_tmp from test group by id, concat(age, floor(1000+rand()*8999))
1, 20_1001, 2
1, 20_1002, 1
第二步:去掉随机数,进行最终的group by —跟原sql的区别是count(0)变成了sum
select id, substr(t.age_tmp, 1, length(t.age_tmp)-5), sum(t.cnt) from
(select id, concat(age, "
”, floor(1000+rand()*8999)) as age_tmp, count(0) as cnt as age_tmp from test group by id, concat(age, floor(1000+rand()8999))) t
group by id, substr(t.age_tmp, 1, length(t.age_tmp)-5)
3、count distinct优化
select count(distinct sid) from sc;
用group by去掉distinct: groupby之后select出来只会出现一次。如果groupby产生倾斜,还可以用上面的随机数
select count(
) from (select sid from sc group by sid) a;
3、队列(queue)是一种常用的数据结构,但是Queue是一个接口,不能直接new,而LinkedList类实现了Queue接口,所以可以new
Queue queue = new LinkedList<>();
queue.offer(“aa”);
queue.offer(“bb”);
Deque接口继承了queue接口,所以它也是一种队列。但是它还自己实现了栈。所以Deque接口既可以实现队列,也可以实现栈。是一种双向队列
Deque stack = new LinkedList<>();
//调用push方法就是栈
stack.push(“aa”);
stack.push(“bb”);
//调用offer方法就是对队列
stack.offer(“aa”);
stack.offer(“bb”);
4、A a = (A)Class.forName(“pacage.A”).newInstance();这和 A a =new A();是一样的效果。
java里面任何class都要装载在虚拟机上才能运行。forName这句话就是装载类用的(new是根据加载到内存中的类创建一个实例,要分清楚)。
5、scala和java中的反射
反射调用,执行Foo类的hello方法:
java实现:
Class class = Class.forName(“Foo”);
Object foo = class.newInstance(); //注意这里的foo不要声明为Foo类型,声明为Object即可,Object是所有类的父类
Method method = class.getMethod(“hello”, String.class);
method.invoke(foo, “Walter”);
scala实现:
val foo = Class.forName(“Foo”).newInstance.asInstanceOf[{ def hello(name: String): String }]
foo.hello(“Walter”)
6、via reflect create singleton
public class test {
private test() {
}
private static test instance = new test();
public static test getInstance() {
return instance;
}

public String parseLine(String str) {
...
}
}

Class c0 = Class.forName(“test”);
Method m0 = c0.getMethod(“getInstance”);
Object instance0 = m0.invoke(new Object()); //调用getInstance()方法,得到单例对象
Method method1 = c0.getMethod(“parseLine”,String.class);
Object result = method1.invoke(instance0, aa); //解析aa字符串,得到解析结果result
7、java反射
对于任意一个类,只要知道类名,就能够通过反射得到这个类的所有属性和方法
反射优点:可以通过配置文件来动态配置和加载类,比如多个任务都会运行主程序,每个任务在主程序里面都要创建不同的解析对象,如果要用new来创建解析对象,必须在主程序里面引用解析类的包,并直接new出解析对象,这样相当于在主程序里面写死解析类。
而用反射就很灵活,不同的任务在配置文件配置不同的解析类名,即可在主程序里面动态地调用解析类及其解析方法。
反射的三种方式:
第一种:  Class clz = Class.forName(“com.entity.Book”);
第二种:  Class clz = Book.class;
第三种: Book book = new Book(); Class clz = book.getClass();
上面只是得到Class对象(包含类的相关信息:方法,属性),要想得到类的实例,需要Object o = clz.newInstance()
在java世界里,一切皆对象。从某种意义上来说,java有两种对象:实例对象和Class对象。每个类的运行时的类型信息就是用Class对象表示的。它包含了与类有关的信息。其实我们的实例对象就通过Class对象来创建的
8、什么时候将java类加载到内存中?
1)编译器,编译代码(calss文件)
2)当你某一个类执行的时候,被调用到了(何种方式调用这里不做考虑),那就是加载内存当中的时候!
而只有真正运行代码时候new类才会初始化对象
9、mysql脏页:当内存数据页和磁盘数据页上的内容不一致时,我们称这个内存页为脏页;
10、innodb存储结构–磁盘结构
表空间:在 InnoDB 存储引擎下,表相关的所有数据(比如业务数据和索引数据)都储存在表空间(tablespace)中。每张表都有一个自己的文件(.ibd)去储存相关数据。表空间又可以细分为segment,extent,page,row。
段:表空间又包含多个段(segment),常见的数据段有:数据段,存储当前表中的数据;索引段,存储当前表中的索引
区:段包含很多个区,每个区始终为 1MB 。区由多个连续连续的页组成,页的大小通常是 16KB,所以一个区可以有 64 (1024/16=64)个连续页。
页:页是 InnoDB 与磁盘交互的最小单位。从磁盘上读取数据,一次性是读取一页数据。将内存中的数据落盘到硬盘上,也是操作一页数据。
行:每页存放一行一行的数据。
11、并发编程的时候经常遇到三个问题:可见性,原子性,有序性问题。也就是并发编程的时候可能这三个性质无法得到保证
用synchronized能保证可见性,原子性,有序性。
volatile只能保证可见性,有序性,不能保证原子性。
CAS能保证原子性,一般都不说CAS能保证可见性和有序性
volatile通常被比喻成"轻量级的synchronized",但是它实际上是不加锁的,不会阻塞线程,和synchronized不同,volatile是一个变量修饰符,只能用来修饰变量。无法修饰方法及代码块等。
volatile的用法比较简单,只需要在声明一个可能被多线程同时访问的变量时,使用volatile修饰就可以了,如下:
volatile int inc = 0;
private volatile static Singleton singleton;
代码书写的顺序与实际执行的顺序不同,指令重排序是编译器或处理器为了提高程序性能而做的优化,但是这就会破坏有序性,有序性即程序执行的顺序按照代码的先后顺序执行。
volatile可以禁止指令重排,这就保证了代码的程序会严格按照代码的先后顺序执行。这就保证了有序性。被volatile修饰的变量的操作,会严格按照代码顺序执行。
synchronized是无法禁止指令重排和处理器优化的。那么他是如何保证的有序性呢?
synchronized保证的有序性是多个线程之间的有序性,即被加锁的内容要按照顺序被多个线程执行。但是单线程其内部的同步代码还是会发生重排序。所以这里的有序性跟volatile有序性是有差别的。
所以有些情况下,比如单例里面,即使用了synchronized,也要结合volatile修饰变量
volatile不能保证变量的复合操作的原子性,比如
volatile int number = 0
number++、count = count*5等操作无法保证原子性
volatile保证可见性:
(1)修改volatile变量时会强制将修改后的值刷新的主内存中。
(2)修改volatile变量后会导致其他线程工作内存中对应的变量值失效(总线嗅探)。因此,再读取该变量值的时候就需要重新从读取主内存中的值。
12、Kimball和Inmon是两种主流的数据仓库方法论
Kimball提出了维度建模方法,将表分为事实表和维度表
kimball模式:适合快速迭代,实施成本低,能够较快交付任务
常见维度模型:星型和雪花。
维度建模4个步骤:选择业务过程或主题(dw是业务过程,dm表是主题), 声明粒度, 确定维度,确定事实
13、dm分为轻度汇总和高度汇总:主要是为了提高表复用性,高度汇总只需要根据轻度汇总写一些简单sql就可以实现,且方便定位问题。
dm为什么分为dma和dmt:方便加工统计。如果只有一层dm,数据量太大,资源压力太大,任务跑太久。不好一口气将所有统计都完成
轻度汇总:以DW为基础,按天进行轻度汇总,一行信息代表一个主题对象一天的汇总行为,例如一个用户一天下单次数。宽表化处理:多业务过程融合(如曝光点击宽表),单业务过程聚合
高度汇总:以轻度汇总或DW为基础,对数据进行累积汇总,一行信息代表一个主题对象的累积行为,例如一个用户从注册那天开始至今一共下了多少次单
14、数据完整性保证:包括条数不能有缺失,以及每条数据里面单个字段不能有缺失。保障机制:1)数据量波动监控—数据量正常了才将任务置为成功。2)关键字段null值监控 3)任务加锁,同一时间只能一个任务执行,任务执行完加依赖,没有依赖下一次执行时将hive表当前分区删除
15、checkpoint的意思就是建立检查点,类似于快照,例如在spark计算里面,计算流程DAG特别长,服务器需要将整个DAG计算完成得出结果,但是如果在这很长的计算流程中突然中间算出的数据丢失了,spark又会根据RDD的依赖关系从头到尾计算一遍,这样子就很费性能,
当然我们可以将中间的计算结果通过cache或者persist放到内存或者磁盘中,但是这样也不能保证数据完全不会丢失,存储的这个内存出问题了或者磁盘坏了,也会导致spark从头再根据RDD计算一遍。
所以就有了checkpoint,其中checkpoint的作用就是将DAG中比较重要的中间数据做一个检查点将结果存储到一个高可用的地方(通常这个地方就是HDFS里面)
spark checkpoint操作需要有action操作激活。checkpoint也是个transformation的算子。
checkpoint原理:等到 Job(第一次计算的Job)结束后另外启动专门的job(第二次) 去完成checkpoint。也就是说需要checkpoint的RDD会被计算两次。因此,在使用rdd.checkpoint() 的时候,建议加上rdd.cache()
用法:
sc.setCheckpointDir(“hdfs://…”)
rdd1.cache()
rdd1.checkpoint
rdd1.count() //触发job,完成第一次计算,此时的计算并不包括checkpoint,等计算完成了,会再起一个job去专门跑checkpoint
16、rdd存放的详细内容:业务数据data、sparkcontext、分区信息、storageLevel(内存or磁盘)、checkpoint信息
17、spark容错机制:
集群容错:Spark会启动多个StandBy Master;
RDD的容错机制:RDD Lineage血统层容错即Lineage重算和checkpoint
18、Checkpoint与持久化的不同:
(1)前者单独存放在高容错的HDFS文件系统,后者放在内存中/磁盘
(2)前者改变了被调用RDD的lineage,后者没有
19、checkpoint保存的就是整个rdd数据
当使用了checkpoint后,数据被保存到HDFS,此RDD的依赖关系也会丢掉,因为数据已经持久化到硬盘,不需要重新计算。
强烈推荐先将数据持久化到内存中(cache操作),否则直接使用checkpoint会开启一个计算,浪费资源。
dd.persist(StorageLevel.DISK_ONLY)与checkpoint区别的:一旦driver program 执行结束,被cache到磁盘上的RDD也会被清空,checkpoint将RDD持久化到HDFS或本地文件夹。如果不手动删除,会一直存在
20、mr优化
map优化

  • 小文件合并:通过参数(mapred.min.split.size,mapred.max.split.size)控制分片大小
  • 增大环形缓冲区大小:io.sort.mb,map数据写磁盘之前,先写到缓冲区BUFFER
  • 设置map端输出压缩:mapred.compress.map.output

reduce优化(包含shuffle)

  • 每一个map都会根据reduce(n)数将map输出结果分成n个partition。为了优化reduce的执行时间,hadoop中等第一个map结束后,所有的reduce就开始尝试从完成的map中下载该reduce对应的partition部分数据。在这个shuffle过程中,由于map的数量通常是很多个的,而每个map中又都有可能包含每个reduce所需要的数据,所以对于每个reduce来说,去各个map中COPY数据也是并行的,可以通过mapred.reduce.parallel.copies这个参数来调整,默认为5。当map数量很多的时候,就可以适当调大这个值,减少shuffle过程使用的时间
  • 和map一样,reduce copy过来的数据也是存入一个buffer中而不是马上写磁盘的,所以我们可以控制这个值来减少IO开销。参数为:mapred.job.shuffle.input.buffer.percent,默认0.7,这是一个百分比,意思是reduce的可用内存中拿出70%作为buffer存放数据

你可能感兴趣的:(大数据架构师之路)