大数据面试之遇见问题及思考
1.hadoop存储一亿数据
考虑:大文件和小文件肯定是要预先设计的,这样就可以调整默认数据块大小,以及可以考虑CombineFile文件压缩,或者归档存储的方式。如果真是上亿个文件信息,一定会导致许多问题,比如性能方面。以及内存中维护目录树能否存储下。推断有可能会有以下情况:a.内存直接溢出,系统报错。2.内存数据,使用虚存。不管是哪种方式,都是不合理的,最合理的应该考虑是都需要分多个集群,或者是hdfs联盟。hadoop3.0中可以使用多个namenode。
1.1 添加硬盘,或者数据目录
关闭datanode进程
修改hdfs-site.xml文件dfs.data.dir值,多个路径以“,”分割
启动datanode进程
注意:关闭datanode进程后,如果namenode长时间未接收到datanode心跳会认为datanode挂了,然后会复制此datanode上的数据到其他主机上。timeout = 10 * 心跳间隔时间(默认3s) + 2 * 检查一次消耗的时间(默认5min)
1.2 添加数据节点
a.新节点环境准备,java,hadoop,ssh,hosts等
b.编辑主节点slaves文件,添加新节点 >hdfs dfsadmin -refreshNodes
c.启动新的数据节点 >hadoop-daemon.sh start datanode 以及 >yarn-daemon.sh start nodemanager
1.3 容量监控
cdh/ambari中可以添加预警邮箱(部署过ambari集群,由于笔记本硬件没部署过cdh集群,一般连接测试使用的原生的,版本号根据cdh提供组件版本)
原生的hadoop集群如果想实现预警的话,可以定时(5-10s,这样也不至于占用太多的带宽)发送get请求,然后xpath解析获取到的页面,获取hdfs利用率,然后在linux下安装一个简单的邮件服务,就可以发送服务情况到某个邮箱,当然这个功能默认肯定是需要关闭的。否者可能会被判断为安全隐患。
另外对于各个主机的运行状态之前项目中使用java开发过一个系统监控的功能,基于snmp协议,获取cpu,内存,网络,磁盘等一系列信息。
2.rowkey原则
a.一般10-100byte,定长
b.开头建议使用散列,避免热key,regionServer负载均衡
c.一般使用散列+时间戳+关键字段
3.hadoop幽灵进程
这个问题我没遇见过,倒是遇见过一次,namenode进程启动之后,进程自动结束了的问题。当时是查找的hdfs的日志,提示的是数据块异常,我本地的环境,默认副本数是1,就直接格式化了。
3.java基本操作kafka
public class ProducerDemo {
private static final String topic = “my-topic”;
public static void main(String[] args) {
Properties properties = new Properties();
properties.put("metadata.broker.list","master:9092,slave1:9092,slave2:9092");
properties.put("serializer.class","kafka.serializer.StringEncoder");
ProducerConfig producerConfig = new ProducerConfig(properties);
Producer<String, String> producer = new Producer<>(producerConfig);
for (int i = 0; i < 1000; i++) {
producer.send(new KeyedMessage<String,String>(topic,"gugu"+i));
}
}
}
// 多线程消费同一个组的数据
public class ConsumerDemo {
private static final String topic = "my-topic";
private static final Integer threads = 2;
public static void main(String[] args) {
Properties properties = new Properties();
properties.put("zookeeper.connect","master:2181,slave1:2181,slave2:2181");
properties.put("group.id","gugu");
//smallest重最开始消费,largest代表重消费者启动后产生的数据才消费
//--from-beginning
properties.put("auto.offset.reset","smallest");
ConsumerConfig config = new ConsumerConfig(properties);
ConsumerConnector consumer = Consumer.createJavaConsumerConnector(config);
Map<String,Integer> topicCountMap = new HashMap<>();
topicCountMap.put(topic,threads);
Map<String, List<KafkaStream<byte[], byte[]>>> messageStreams = consumer.createMessageStreams(topicCountMap);
List<KafkaStream<byte[], byte[]>> kafkaStreams = messageStreams.get(topic);
for (KafkaStream<byte[], byte[]> kafkaStream:kafkaStreams){
new Thread(new Runnable() {
@Override
public void run() {
for (MessageAndMetadata<byte[], byte[]> mm:kafkaStream){
String msg = new String(mm.message());
System.out.println(msg);
}
}
}).start();
}
}
}
kafka的一个topic可以有多个分区,可以通过多个分区将一个topic的数据打散到多个broker上,实现负载均衡,以提高吞吐量。
经过查阅资料,确实发现了与kafka的两个offset,一个是存储在zookeeper中的和另外一个存储在kafka中的。如果是使用javaapi来消费的,需要配置zookeeper.connect,这个时候使用的是zookeeper中的offset。如果使用kafka默认api,需要配置bootstrap.servers参数,这个时候使用kafka中存储的offset。
4.spark读取数据
kafkaUtils.createStream
较顶层,但是如果要保证保证数据可靠性(job出错的时候可能会丢失),需要开启WAL和checkpiont,因此会影响效率
kafkaUtils.createDirectStream
底层方法会根据kafka分区创建rdd,因此效率比较高,如果需要保证可靠性,需要开启checkpiont