近期学习笔记整理

3-26

Hbase查询性能优化

hive和hbase分别适用于分析查询和实时查询

hbase查询优化网址:https://www.cnblogs.com/xjh713/p/7750134.html

hbase万亿性能优化总结:https://blog.csdn.net/odailidong/article/details/41794403

file.block.cache.size:RS的blockcache的内存大小限制,默认值0.25,在偏向读的业务中,可以适当调大该值,具体配置时需试hbase集群服务的业务特征,结合memstore的内存占比进行综合考虑。

hbase.regionserver.global.memstore.upperLimit:默认值0.4,RS所有memstore占用内存在总内存中的upper比例,当达到该值,则会从整个RS中找出最需要flush的region进行flush,直到总内存比例降至该数限制以下,并且在降至限制比例以下前将阻塞所有的写memstore的操作,在以写为主的集群中,可以调大该配置项,不建议太大,因为block cache和memstore cache的总大小不会超过0.8,而且不建议这两个cache的大小总和达到或者接近0.8,避免OOM,在偏向写的业务时,可配置为0.45,memstore.lowerLimit保持0.35不变,在偏向读的业务中,可调低为0.35,同时memstore.lowerLimit调低为0.3,或者再向下0.05个点,不能太低,除非只有很小的写入操作,如果是兼顾读写,则采用默认值即可。

hbase.regionserver.global.memstore.lowerLimit:默认值0.35,RS的所有memstore占用内存在总内存中的lower比例,当达到该值,则会从整个RS中找出最需要flush的region进行flush,配置时需结合memstore.upperLimit和block cache的配置。

Hbase调优:

  • 1.hbase.regionserver.handler.count:rpc请求的线程数量,默认值是10,生产环境建议使用100,也不是越大越好,特别是当请求内容很大的时候,比如scan/put几M的数据,会占用过多的内存,有可能导致频繁的GC,甚至出现内存溢出。

  • 2.hbase.regionserver.hlog.splitlog.writer.threads:默认值是3,建议设为10,日志切割所用的线程数。

  • 3.hbase.hregion.majorcompaction:hbase的region主合并的间隔时间,默认为1天,建议设置为0,禁止自动的major主合并,major合并会把一个store下所有的storefile重写为一个storefile文件,在合并过程中还会把有删除标识的数据删除,在生产集群中,主合并能持续数小时之久,为减少对业务的影响,建议在业务低峰期进行手动或者通过脚本或者api定期进行major合并。

  • 4.hbase.hregion.memstore.flush.size:默认值128M,单位字节,一旦有memstore超过该值将被flush,如果regionserver的jvm内存比较充足(16G以上),可以调整为256M。

  • 5.hbase.regionserver.global.memstore.upperLimit:(0.35)默认值0.4,regionserver所有memstore占用内存在总内存中的upper比例,当达到该值,则会从整个regionserver中找出最需要flush的region进行flush,直到总内存比例降到该数以下,采用默认值即可。memstore.lowerLimit0.3

  • 6.hbase.regionserver.global.memstore.lowerLimit:默认值0.35,采用默认值即可。

  • 7.hfile.block.cache.size:(0.4)默认值0.25,regionserver的block cache的内存大小限制,在偏向读的业务中,可以适当调大该值,需要注意的是hbase.regionserver.global.memstore.upperLimit的值和hfile.block.cache.size的值之和必须小于0.8。

  • 8.hbase.client.scanner.caching:scan缓存,默认为1,避免占用过多的client和rs的内存,一般1000以内合理,如果一条数据太大,则应该设置一个较小的值,通常是设置业务需求的一次查询的数据条数,如果是扫描数据对下次查询没有帮助,则可以设置scan的setCacheBlocks为false,避免使用缓存

  • 9.建表注意事项:开启压缩 合理的设计rowkey 进行预分区 开启bloomfilter

  • 10.hbase.hregion.max.filesize配置region大小,0.94.12版本默认是10G,region的大小与集群支持的总数据量有关系,如果总数据量小,则单个region太大,不利于并行的数据处理,如果集群需支持的总数据量比较大,region太小,则会导致region的个数过多,导致region的管理等成本过高,如果一个RS配置的磁盘总量为3T*12=36T数据量,数据复制3份,则一台RS服务器可以存储10T的数据,如果每个region最大为10G,则最多1000个region,如此看,94.12的这个默认配置还是比较合适的,不过如果要自己管理split,则应该调大该值,并且在建表时规划好region数量和rowkey设计,进行region预建,做到一定时间内,每个region的数据大小在一定的数据量之下,当发现有大的region,或者需要对整个表进行region扩充时再进行split操作,一般提供在线服务的hbase集群均会弃用hbase的自动split,转而自己管理split。

  • 11.hbase.ipc.client.tcpnodelay:默认是false,建议设为true,关闭消息缓冲

3-28

  • 直接建表 PARAM_KEY:transient_lastDdTime

  • 建表之后load操作 PARAM_KEY:transient_lastDdTime,numFiles,totalSize=hdfs上文件大小

  • 建表之后Insert操作,数据源来自load表,PARAM_KEY:transient_lastDdTime,numFiles,totalSize=hive目录下文件大小,numRows(准确),rawDataSize(准确)

  • 建表之后load之后insert PARAM_KEY:transient_lastDdTime,numFiles,totalSize=hive表目录下文件总大小,numRows=insert文件行数(不准确),rawDataSize=insert文件大小

  • 建表之后insert再load PARAM_KEY:transient_lastDdTime,numFiles,totalSize=hive表目录下文件总大小,numRows=0,rawDataSize=0

  • 建表之后insert、load、insert PARAM_KEY:transient_lastDdTime,numFiles,totalSize=hive表目录下文件总大小,numRows=当前insert文件行数,rawDataSize=当前insert文件rawDataSize

ANALYZE TABLE xzxx COMPUTE STATISTICS;进行统计信息的处理

load数据通过Mysql无法访问到列信息(统计信息)

经测试,NumRows行数信息可以通过mysql与hive方法获得,mysql获取时间较快,但是load与insert载入数据方式不同,通过Insert操作可以在mysql中获得准确信息,通过load操作会使得numRows行数信息不准确,建议执行、ANALYZE TABLE xxx COMPUTE STATISTICS后,再通过mysql查询列信息

此外,两种操作均可以在mysql获得准确的totalsize文件大小参数

IO:同步异步和阻塞非阻塞区别:同步和异步指用户和内核交互方式,阻塞和非阻塞指IO调用的方式。

  • IO多路复用是异步阻塞的,因为select相当于同时阻塞多个socket轮询来查看时候数据准备完毕,但是异步是指用户线程不必阻塞,通过reactor设计模式,只要注册事件之后,轮询操作由enventhandler来完成。

select\poll\epoll区别:

从区别来看,poll和select类似,只不过poll没有文件描述符限制,因为是一种类似于链表的结构,缺点就是每次调用二者都需要将fdset从用户空间拷贝到内核空间这样做是无意义的,并且每次调用select都需要轮询所有的fdset,以便查看数据是否准备就绪,而在epoll中便很好的解决了这个问题,因为每个需要挂一遍这个是必不可少的,之后给每个注册一个回掉函数,当有数据就绪的时候,便把可读取的挂到一个队列当中,因此仅查看就绪队列时候有可读取的socket就可以了。

面经总结贴:https://www.nowcoder.com/discuss/167046

3-29

面经汇总地址:https://www.nowcoder.com/discuss/167046?type=0&order=4&pos=23&page=1

反射和代理

  • 反射:运行时知道类的方法和属性,运行时调用对象的方法和属性

    • 正射:编译器在编译时打开和检查.class文件

    • 反射:运行时打开和检查.class文件

mapreduce的join操作优化

  • reduce join(网络传输效率最低)

  • map join(大表对小表:小表加载到内存当中)

  • semi join(大表对大表:筛选key)

hive中的join操作

尽量小表join大表,会将小表缓存


 /*

   线程池模型模型:使用{java.util.concurrent.Executors}实现

 */

 import java.util.concurrent.ExecutorService;

 import java.util.concurrent.Executors;



 class MyRunnable implements Runnable {

    @Override

    public void run() {

        for (int x = 0; x < 100; x++) {

            System.out.println(Thread.currentThread().getName() + ":" + x);

       }

    }

 }


 public class ExecutorServiceDemo {

    public static void main(String[] args) {

     // 创建一个线程池对象,控制要创建几个线程对象。

     // public static ExecutorService newFixedThreadPool(int nThreads)

     ExecutorService pool = Executors.newFixedThreadPool(2);



     // 可以执行Runnable对象或者Callable对象代表的线程

     pool.submit(new MyRunnable());

     pool.submit(new MyRunnable());



    //结束线程池

    pool.shutdown();

   }

 }


 /*

   生产者消费者模型:使用{java.util.concurrent.BlockingQueue}实现

 */

 import java.util.concurrent.BlockingQueue;

 import java.util.concurrent.LinkedBlockingQueue;

 import java.util.logging.Level;

 import java.util.logging.Logger;

 public class ProducerConsumerPattern {

 

    public static void main(String args[]){

 

     //Creating shared object

     BlockingQueue sharedQueue = new LinkedBlockingQueue();

 

     //Creating Producer and Consumer Thread

     Thread prodThread = new Thread(new Producer(sharedQueue));

     Thread consThread = new Thread(new Consumer(sharedQueue));

 

     //Starting producer and Consumer thread

     prodThread.start();

     consThread.start();

    }

 

 }

 

 //Producer Class in java

 class Producer implements Runnable {

 

    private final BlockingQueue sharedQueue;

 

    public Producer(BlockingQueue sharedQueue) {

        this.sharedQueue = sharedQueue;

    }

 

    @Override

    public void run() {

        for(int i=0; i<10; i++){

            try {

                System.out.println("Produced: " + i);

                sharedQueue.put(i);

            } catch (InterruptedException ex) {

                Logger.getLogger(Producer.class.getName()).log(Level.SEVERE, null, ex);

            }

        }

    }

 

 }

 

 //Consumer Class in Java

 class Consumer implements Runnable{

 

    private final BlockingQueue sharedQueue;

 

    public Consumer (BlockingQueue sharedQueue) {

        this.sharedQueue = sharedQueue;

    }

 

    @Override

    public void run() {

        while(true){

            try {

                System.out.println("Consumed: "+ sharedQueue.take());

            } catch (InterruptedException ex) {

                Logger.getLogger(Consumer.class.getName()).log(Level.SEVERE, null, ex);

            }

        }

    }

 

 }

4-16

关于hadoop ha,当namenode写editlog时,NameNode会同时向所有JournalNode并行写文件,只要有N/2+1结点写成功则认为此次写操作成功,遵循paxos协议。

NameNode会把edit。

为了保持备用节点和活动节点的同步,需要两个节点来共同访问一个共享存储设备到一个目录。

为了提供快速的故障转移,必须保证备用节点有最新的集群的块的位置信息,为了达到这一点,datanode节点需要配置两个namenode的位置,同时发送块的位置信息和心跳信息到两个nameNode。(即心跳和块报告)

hbase regionsever向zookeeper注册,提供hbase regionsever状态信息(是否在线),hmaster启动时候会将habse系统表-Root-加载到zookeeper cluster,通过zookeeper cluster可以获取当前系统表.META.存储对应的regionserver信息。HMaster主要作用在于,通过HMASTER维护系统表-Root-,META,记录regionsever所对应region变化信息。此外还负责监控处理当前hbase cluster中regionsever状态变化信息。

总结三点(zookeeper在hbase中):

1.HRegion的寻址入口(即root表的地址的)

2.实时监控HRegion的上线下线信息

3.HBase中几乎所有的元数据存储都是放在ZooKeeper上

我们使用spark-submit提交一个Spark作业之后,这个作业就会启动一个对应的Driver进程。根据你使用的部署模式(deploy-mode)不同,Driver进程可能在本地启动,也可能在集群中某个工作节点上启动。Driver进程本身会根据我们设置的参数,占有一定数量的内存和CPU core。而Driver进程要做的第一件事情,就是向集群管理器(可以是Spark Standalone集群,也可以是其他的资源管理集群,美团•大众点评使用的是YARN作为资源管理集群)申请运行Spark作业需要使用的资源,这里的资源指的就是Executor进程。YARN集群管理器会根据我们为Spark作业设置的资源参数,在各个工作节点上,启动一定数量的Executor进程,每个Executor进程都占有一定数量的内存和CPU core。

在申请到了作业执行所需的资源之后,Driver进程就会开始调度和执行我们编写的作业代码了。Driver进程会将我们编写的Spark作业代码分拆为多个stage,每个stage执行一部分代码片段,并为每个stage创建一批task,然后将这些task分配到各个Executor进程中执行。task是最小的计算单元,负责执行一模一样的计算逻辑(也就是我们自己编写的某个代码片段),只是每个task处理的数据不同而已。一个stage的所有task都执行完毕之后,会在各个节点本地的磁盘文件中写入计算中间结果,然后Driver就会调度运行下一个stage。下一个stage的task的输入数据就是上一个stage输出的中间结果。如此循环往复,直到将我们自己编写的代码逻辑全部执行完,并且计算完所有的数据,得到我们想要的结果为止。

4-17

spark运行流程:

1.Client运行时向Master发送驱动申请(发送RequestSubmitDriver指令)

2.Master调度可用Worker资源进行驱动安装(发送LaunchDriver)

3.Worker运行DriverRunner进行驱动加载,并向Master发送应用注册请求(发送RegisterApplication指令)

4.Master调度可用Worker资源进行应用的Executor安装(发送LaunchExecutor指令)

5.Executor安装完毕后向Driver注册驱动可用Excutor资源(发送RegisterExecutor指令)

6.最后是运行用户代码时,通过DAGScheduler,TaskScheduler封装为可以执行的TaskSetManager对象

7.TaskSetManager对象与Driver中的Executor资源进行匹配,在队形的Exxcutor中发布任务(发送LaunchTaskz指令)

8.TaskRunner执行完毕后,调用DriverRunner提交给DAGScheduler,循环7直到完成

4-19

public class Singleton {

     volitale private static Singleton instance;  

     private Singleton (){

     }   

     public static Singleton getInstance(){    //对获取实例的方法进行同步

       if (instance == null){

           synchronized(Singleton.class){

               if (instance == null)

                   instance = new Singleton(); 

           }

       }

       return instance;

     }
     
 }