这一章节主要讲解Hbase的内部的Mapreduce过程。
1)hbase 可以作为数据源,
2)hbase作为输出源
3)hbase数据转移。
1)hbase 可以作为数据源,Export.java
  1. public static Job createSubmittableJob(Configuration conf, String[] args)
  2. throws IOException {
  3. String tableName = args[0];
  4. Path outputDir = new Path(args[1]);
  5. Job job = new Job(conf, NAME + "_" + tableName);
  6. job.setJobName(NAME + "_" + tableName);
  7. job.setJarByClass(Export.class);
  8. // 定义scan。主要根据配置是否需要设置fitle ,startkey,endkey等。
  9.     //简单 Scan s =new Scan()
  10. Scan s = getConfiguredScanForJob(conf, args);
  11. //这里会定义每一个region一个map。map的数量等于region的数量。这个map里面基本什么都没做就是读到的
  12. //数据直接写出。
  13. //这里会定义map的输入格式为TableInputFormat.class
  14. IdentityTableMapper.initJob(tableName, s, IdentityTableMapper.class, job);
  15. // No reducers. Just write straight to output files.
  16. //直接保存数据。
  17. job.setNumReduceTasks(0);
  18. //输出文件
  19. job.setOutputFormatClass(SequenceFileOutputFormat.class);
  20. job.setOutputKeyClass(ImmutableBytesWritable.class);
  21. job.setOutputValueClass(Result.class);
  22. FileOutputFormat.setOutputPath(job, outputDir); // job conf doesn't contain the conf so doesn't have a default fs.
  23. return job;
  24. }
这个mapreduce里面最重要的是怎么确定一个region 对应一个map。这就是靠TableInputFormat决定的
  1. @Override
  2. public List<InputSplit> getSplits(JobContext context) throws IOException {
  3. List<InputSplit> splits = super.getSplits(context);
  4. if ((conf.get(SHUFFLE_MAPS) != null) && "true".equals(conf.get(SHUFFLE_MAPS).toLowerCase())) {
  5. Collections.shuffle(splits);
  6. }
  7. return splits;
  8. }
这里getSplits是根据regionLocationInfo ,分区当然是startkey。根据region的数量设置map的个数,这样就可以一个region
对应一个map了。当然这里没有设置,因为没必要。
在初始化map的时候设置了combinerClass为putCombiner
  1. @Override
  2. protected void reduce(K row, Iterable<Put> vals, Context context)
  3. throws IOException, InterruptedException {
  4. long threshold = context.getConfiguration().getLong(
  5. "putcombiner.row.threshold", 1L * (1<<30));
  6. int cnt = 0;
  7. long curSize = 0;
  8. Put put = null;
  9. Map<byte[], List<Cell>> familyMap = null;
  10. for (Put p : vals) {
  11. cnt++;
  12. if (put == null) {
  13. put = p;
  14. familyMap = put.getFamilyCellMap();
  15. } else {
  16. for (Entry<byte[], List<Cell>> entry : p.getFamilyCellMap()
  17. .entrySet()) {
  18. List<Cell> cells = familyMap.get(entry.getKey());
  19. List<Cell> kvs = (cells != null) ? (List<Cell>) cells : null;
  20. for (Cell cell : entry.getValue()) {
  21. KeyValue kv = KeyValueUtil.ensureKeyValueTypeForMR(cell);
  22. curSize += kv.heapSize();
  23. if (kvs != null) {
  24. kvs.add(kv);
  25. }
  26. }
  27. if (cells == null) {
  28. familyMap.put(entry.getKey(), entry.getValue());
  29. }
  30. }
  31. if (cnt % 10 == 0) context.setStatus("Combine " + cnt);
  32. if (curSize > threshold) {
  33. if (LOG.isDebugEnabled()) {
  34. LOG.debug(String.format("Combined %d Put(s) into %d.", cnt, 1));
  35. }
  36. context.write(row, put);
  37. put = null;
  38. curSize = 0;
  39. cnt = 0;
  40. }
  41. }
  42. }
  43. if (put != null) {
  44. if (LOG.isDebugEnabled()) {
  45. LOG.debug(String.format("Combined %d Put(s) into %d.", cnt, 1));
  46. }
  47. context.write(row, put);
  48. }
  49. }
因为hbase 输出都是一个cell单元,如果一行记录包含多个列,就需要这个东西。将相同rowkey的数据放在一块。
对于reduce 根本不需,指定输出格式就行。然后就是位置。
这样Export 过程结束:
2)Import.java
这个刚好相反。需要关注reduce过程
  1. public static Job createSubmittableJob(Configuration conf, String[] args)
  2. throws IOException {
  3. TableName tableName = TableName.valueOf(args[0]);
  4. conf.set(TABLE_NAME, tableName.getNameAsString());
  5. Path inputDir = new Path(args[1]);
  6. Job job = new Job(conf, NAME + "_" + tableName);
  7. job.setJarByClass(Importer.class);
  8. FileInputFormat.setInputPaths(job, inputDir);
  9. job.setInputFormatClass(SequenceFileInputFormat.class);
  10. String hfileOutPath = conf.get(BULK_OUTPUT_CONF_KEY);
  11. // make sure we get the filter in the jars
  12. try {
  13. Class<? extends Filter> filter = conf.getClass(FILTER_CLASS_CONF_KEY, null, Filter.class);
  14. if (filter != null) {
  15. TableMapReduceUtil.addDependencyJarsForClasses(conf, filter);
  16. }
  17. } catch (Exception e) {
  18. throw new IOException(e);
  19. }
  20. //这里直接写出kv文件。因为数据量大。按region分了
  21. if (hfileOutPath != null && conf.getBoolean(HAS_LARGE_RESULT, false)) {
  22. LOG.info("Use Large Result!!");
  23. try (Connection conn = ConnectionFactory.createConnection(conf);
  24. Table table = conn.getTable(tableName);
  25. RegionLocator regionLocator = conn.getRegionLocator(tableName)) {
  26. HFileOutputFormat2.configureIncrementalLoad(job, table.getTableDescriptor(), regionLocator);
  27. job.setMapperClass(KeyValueSortImporter.class);
  28. job.setReducerClass(KeyValueReducer.class);
  29. Path outputDir = new Path(hfileOutPath);
  30. FileOutputFormat.setOutputPath(job, outputDir);
  31. job.setMapOutputKeyClass(KeyValueWritableComparable.class);
  32. job.setMapOutputValueClass(KeyValue.class);
  33. job.getConfiguration().setClass("mapreduce.job.output.key.comparator.class",
  34. KeyValueWritableComparable.KeyValueWritableComparator.class,
  35. RawComparator.class);
  36. Path partitionsPath =
  37. new Path(TotalOrderPartitioner.getPartitionFile(job.getConfiguration()));
  38. FileSystem fs = FileSystem.get(job.getConfiguration());
  39. fs.deleteOnExit(partitionsPath);
  40. job.setPartitionerClass(KeyValueWritableComparablePartitioner.class);
  41. job.setNumReduceTasks(regionLocator.getStartKeys().length);
  42. TableMapReduceUtil.addDependencyJarsForClasses(job.getConfiguration(),
  43. com.google.common.base.Preconditions.class);
  44. }
  45. //没有分区。
  46. } else if (hfileOutPath != null) {
  47. job.setMapperClass(KeyValueImporter.class);
  48. try (Connection conn = ConnectionFactory.createConnection(conf);
  49. Table table = conn.getTable(tableName);
  50. RegionLocator regionLocator = conn.getRegionLocator(tableName)){
  51. job.setReducerClass(KeyValueSortReducer.class);
  52. Path outputDir = new Path(hfileOutPath);
  53. FileOutputFormat.setOutputPath(job, outputDir);
  54. job.setMapOutputKeyClass(ImmutableBytesWritable.class);
  55. job.setMapOutputValueClass(KeyValue.class);
  56. HFileOutputFormat2.configureIncrementalLoad(job, table.getTableDescriptor(), regionLocator);
  57. TableMapReduceUtil.addDependencyJarsForClasses(job.getConfiguration(),
  58. com.google.common.base.Preconditions.class);
  59. }
  60. } else {
  61. //这个直接调用内TableOutputFarmat。这样就直接调用的是put。这个少量还好,多了不行。
  62. //具体的write见下面的代码
  63. // No reducers. Just write straight to table. Call initTableReducerJob
  64. // because it sets up the TableOutputFormat.
  65. job.setMapperClass(Importer.class);
  66. TableMapReduceUtil.initTableReducerJob(tableName.getNameAsString(), null, job);
  67. job.setNumReduceTasks(0);
  68. }
  69. return job;
  70. }
这个主要的就是标红的地方,定义reduce的个数,定义reduce的输出是按region来分区的。这样就ok了。
这里的partition也是按照startkey来区分的
  1. private static KeyValueWritableComparable[] START_KEYS = null;
  2. @Override
  3. public int getPartition(KeyValueWritableComparable key, KeyValue value,
  4. int numPartitions) {
  5. for (int i = 0; i < START_KEYS.length; ++i) {
  6. if (key.compareTo(START_KEYS[i]) <= 0) {
  7. return i;
  8. }
  9. }
  10. return START_KEYS.length;
  11. }
  12. }
  1. @Override
  2. public void write(KEY key, Mutation value)
  3. throws IOException {
  4. if (!(value instanceof Put) && !(value instanceof Delete)) {
  5. throw new IOException("Pass a Delete or a Put");
  6. }
  7. mutator.mutate(value);
  8. }
生成的kv文件怎么load到hbase里面呢,需要调用另外一个类LoadIncrementalHFiles
LoadIncrementalHFiles
LoadIncrementalHFiles
LoadIncrementalHFiles
LoadIncrementalHFiles
LoadIncrementalHFiles
重要的东西多说一点。
然后第三种表之间copy 用CopyTable
到此结束。下面是拷贝过来的。算是总结了
在对于大量的数据导入到Hbase中, 如果一条一条进行插入, 则太耗时了, 所以可以先采用MapReduce生成HFile文件, 然后使用BulkLoad导入hbase中. 引用:一、这种方式有很多的优点:1. 如果我们一次性入库hbase巨量数据,处理速度慢不说,还特别占用Region资源, 一个比较高效便捷的方法就是使用 “Bulk Loading”方法,即HBase提供的HFileOutputFormat类。2. 它是利用hbase的数据信息按照特定格式存储在hdfs内这一原理,直接生成这种hdfs内存储的数据格式文件,然后上传至合适位置,即完成巨量数据快速入库的办法。配合mapreduce完成,高效便捷,而且不占用region资源,增添负载。二、这种方式也有很大的限制:1. 仅适合初次数据导入,即表内数据为空,或者每次入库表内都无数据的情况。2. HBase集群与Hadoop集群为同一集群,即HBase所基于的HDFS为生成HFile的MR的集群.

这一章节主要讲解Hbase的内部的Mapreduce过程。
1)hbase 可以作为数据源,
2)hbase作为输出源
3)hbase数据转移。
1)hbase 可以作为数据源,Export.java
  1. public static Job createSubmittableJob(Configuration conf, String[] args)
  2. throws IOException {
  3. String tableName = args[0];
  4. Path outputDir = new Path(args[1]);
  5. Job job = new Job(conf, NAME + "_" + tableName);
  6. job.setJobName(NAME + "_" + tableName);
  7. job.setJarByClass(Export.class);
  8. // 定义scan。主要根据配置是否需要设置fitle ,startkey,endkey等。
  9.     //简单 Scan s =new Scan()
  10. Scan s = getConfiguredScanForJob(conf, args);
  11. //这里会定义每一个region一个map。map的数量等于region的数量。这个map里面基本什么都没做就是读到的
  12. //数据直接写出。
  13. //这里会定义map的输入格式为TableInputFormat.class
  14. IdentityTableMapper.initJob(tableName, s, IdentityTableMapper.class, job);
  15. // No reducers. Just write straight to output files.
  16. //直接保存数据。
  17. job.setNumReduceTasks(0);
  18. //输出文件
  19. job.setOutputFormatClass(SequenceFileOutputFormat.class);
  20. job.setOutputKeyClass(ImmutableBytesWritable.class);
  21. job.setOutputValueClass(Result.class);
  22. FileOutputFormat.setOutputPath(job, outputDir); // job conf doesn't contain the conf so doesn't have a default fs.
  23. return job;
  24. }
这个mapreduce里面最重要的是怎么确定一个region 对应一个map。这就是靠TableInputFormat决定的

  1. @Override
  2. public List<InputSplit> getSplits(JobContext context) throws IOException {
  3. List<InputSplit> splits = super.getSplits(context);
  4. if ((conf.get(SHUFFLE_MAPS) != null) && "true".equals(conf.get(SHUFFLE_MAPS).toLowerCase())) {
  5. Collections.shuffle(splits);
  6. }
  7. return splits;
  8. }

这里getSplits是根据regionLocationInfo ,分区当然是startkey。根据region的数量设置map的个数,这样就可以一个region
对应一个map了。当然这里没有设置,因为没必要。

在初始化map的时候设置了combinerClass为putCombiner
  1. @Override
  2. protected void reduce(K row, Iterable<Put> vals, Context context)
  3. throws IOException, InterruptedException {
  4. long threshold = context.getConfiguration().getLong(
  5. "putcombiner.row.threshold", 1L * (1<<30));
  6. int cnt = 0;
  7. long curSize = 0;
  8. Put put = null;
  9. Map<byte[], List<Cell>> familyMap = null;
  10. for (Put p : vals) {
  11. cnt++;
  12. if (put == null) {
  13. put = p;
  14. familyMap = put.getFamilyCellMap();
  15. } else {
  16. for (Entry<byte[], List<Cell>> entry : p.getFamilyCellMap()
  17. .entrySet()) {
  18. List<Cell> cells = familyMap.get(entry.getKey());
  19. List<Cell> kvs = (cells != null) ? (List<Cell>) cells : null;
  20. for (Cell cell : entry.getValue()) {
  21. KeyValue kv = KeyValueUtil.ensureKeyValueTypeForMR(cell);
  22. curSize += kv.heapSize();
  23. if (kvs != null) {
  24. kvs.add(kv);
  25. }
  26. }
  27. if (cells == null) {
  28. familyMap.put(entry.getKey(), entry.getValue());
  29. }
  30. }
  31. if (cnt % 10 == 0) context.setStatus("Combine " + cnt);
  32. if (curSize > threshold) {
  33. if (LOG.isDebugEnabled()) {
  34. LOG.debug(String.format("Combined %d Put(s) into %d.", cnt, 1));
  35. }
  36. context.write(row, put);
  37. put = null;
  38. curSize = 0;
  39. cnt = 0;
  40. }
  41. }
  42. }
  43. if (put != null) {
  44. if (LOG.isDebugEnabled()) {
  45. LOG.debug(String.format("Combined %d Put(s) into %d.", cnt, 1));
  46. }
  47. context.write(row, put);
  48. }
  49. }
因为hbase 输出都是一个cell单元,如果一行记录包含多个列,就需要这个东西。将相同rowkey的数据放在一块。

对于reduce 根本不需,指定输出格式就行。然后就是位置。

这样Export 过程结束:

2)Import.java
这个刚好相反。需要关注reduce过程
  1. public static Job createSubmittableJob(Configuration conf, String[] args)
  2. throws IOException {
  3. TableName tableName = TableName.valueOf(args[0]);
  4. conf.set(TABLE_NAME, tableName.getNameAsString());
  5. Path inputDir = new Path(args[1]);
  6. Job job = new Job(conf, NAME + "_" + tableName);
  7. job.setJarByClass(Importer.class);
  8. FileInputFormat.setInputPaths(job, inputDir);
  9. job.setInputFormatClass(SequenceFileInputFormat.class);
  10. String hfileOutPath = conf.get(BULK_OUTPUT_CONF_KEY);
  11. // make sure we get the filter in the jars
  12. try {
  13. Class<? extends Filter> filter = conf.getClass(FILTER_CLASS_CONF_KEY, null, Filter.class);
  14. if (filter != null) {
  15. TableMapReduceUtil.addDependencyJarsForClasses(conf, filter);
  16. }
  17. } catch (Exception e) {
  18. throw new IOException(e);
  19. }
  20. //这里直接写出kv文件。因为数据量大。按region分了
  21. if (hfileOutPath != null && conf.getBoolean(HAS_LARGE_RESULT, false)) {
  22. LOG.info("Use Large Result!!");
  23. try (Connection conn = ConnectionFactory.createConnection(conf);
  24. Table table = conn.getTable(tableName);
  25. RegionLocator regionLocator = conn.getRegionLocator(tableName)) {
  26. HFileOutputFormat2.configureIncrementalLoad(job, table.getTableDescriptor(), regionLocator);
  27. job.setMapperClass(KeyValueSortImporter.class);
  28. job.setReducerClass(KeyValueReducer.class);
  29. Path outputDir = new Path(hfileOutPath);
  30. FileOutputFormat.setOutputPath(job, outputDir);
  31. job.setMapOutputKeyClass(KeyValueWritableComparable.class);
  32. job.setMapOutputValueClass(KeyValue.class);
  33. job.getConfiguration().setClass("mapreduce.job.output.key.comparator.class",
  34. KeyValueWritableComparable.KeyValueWritableComparator.class,
  35. RawComparator.class);
  36. Path partitionsPath =
  37. new Path(TotalOrderPartitioner.getPartitionFile(job.getConfiguration()));
  38. FileSystem fs = FileSystem.get(job.getConfiguration());
  39. fs.deleteOnExit(partitionsPath);
  40. job.setPartitionerClass(KeyValueWritableComparablePartitioner.class);
  41. job.setNumReduceTasks(regionLocator.getStartKeys().length);
  42. TableMapReduceUtil.addDependencyJarsForClasses(job.getConfiguration(),
  43. com.google.common.base.Preconditions.class);
  44. }
  45. //没有分区。
  46. } else if (hfileOutPath != null) {
  47. job.setMapperClass(KeyValueImporter.class);
  48. try (Connection conn = ConnectionFactory.createConnection(conf);
  49. Table table = conn.getTable(tableName);
  50. RegionLocator regionLocator = conn.getRegionLocator(tableName)){
  51. job.setReducerClass(KeyValueSortReducer.class);
  52. Path outputDir = new Path(hfileOutPath);
  53. FileOutputFormat.setOutputPath(job, outputDir);
  54. job.setMapOutputKeyClass(ImmutableBytesWritable.class);
  55. job.setMapOutputValueClass(KeyValue.class);
  56. HFileOutputFormat2.configureIncrementalLoad(job, table.getTableDescriptor(), regionLocator);
  57. TableMapReduceUtil.addDependencyJarsForClasses(job.getConfiguration(),
  58. com.google.common.base.Preconditions.class);
  59. }
  60. } else {
  61. //这个直接调用内TableOutputFarmat。这样就直接调用的是put。这个少量还好,多了不行。
  62. //具体的write见下面的代码
  63. // No reducers. Just write straight to table. Call initTableReducerJob
  64. // because it sets up the TableOutputFormat.
  65. job.setMapperClass(Importer.class);
  66. TableMapReduceUtil.initTableReducerJob(tableName.getNameAsString(), null, job);
  67. job.setNumReduceTasks(0);
  68. }
  69. return job;
  70. }
这个主要的就是标红的地方,定义reduce的个数,定义reduce的输出是按region来分区的。这样就ok了。
这里的partition也是按照startkey来区分的
  1. private static KeyValueWritableComparable[] START_KEYS = null;
  2. @Override
  3. public int getPartition(KeyValueWritableComparable key, KeyValue value,
  4. int numPartitions) {
  5. for (int i = 0; i < START_KEYS.length; ++i) {
  6. if (key.compareTo(START_KEYS[i]) <= 0) {
  7. return i;
  8. }
  9. }
  10. return START_KEYS.length;
  11. }
  12. }
  1. @Override
  2. public void write(KEY key, Mutation value)
  3. throws IOException {
  4. if (!(value instanceof Put) && !(value instanceof Delete)) {
  5. throw new IOException("Pass a Delete or a Put");
  6. }
  7. mutator.mutate(value);
  8. }
生成的kv文件怎么load到hbase里面呢,需要调用另外一个类LoadIncrementalHFiles
LoadIncrementalHFiles
LoadIncrementalHFiles
LoadIncrementalHFiles
LoadIncrementalHFiles
LoadIncrementalHFiles
重要的东西多说一点。
然后第三种表之间copy 用CopyTable
到此结束。下面是拷贝过来的。算是总结了
在对于大量的数据导入到Hbase中, 如果一条一条进行插入, 则太耗时了, 所以可以先采用MapReduce生成HFile文件, 然后使用BulkLoad导入hbase中. 引用:一、这种方式有很多的优点:1. 如果我们一次性入库hbase巨量数据,处理速度慢不说,还特别占用Region资源, 一个比较高效便捷的方法就是使用 “Bulk Loading”方法,即HBase提供的HFileOutputFormat类。2. 它是利用hbase的数据信息按照特定格式存储在hdfs内这一原理,直接生成这种hdfs内存储的数据格式文件,然后上传至合适位置,即完成巨量数据快速入库的办法。配合mapreduce完成,高效便捷,而且不占用region资源,增添负载。二、这种方式也有很大的限制:1. 仅适合初次数据导入,即表内数据为空,或者每次入库表内都无数据的情况。2. HBase集群与Hadoop集群为同一集群,即HBase所基于的HDFS为生成HFile的MR的集群.



hbase 源代码分析 (17)MapReduce 过程相关推荐

  1. HBase源代码分析之MemStore的flush发起时机、推断条件等详情(二)

    在<HBase源代码分析之MemStore的flush发起时机.推断条件等详情>一文中,我们具体介绍了MemStore flush的发起时机.推断条件等详情.主要是两类操作.一是会引起Me ...

  2. 【Java】【Flume】Flume-NG源代码分析的启动过程(两)

    本节分析配置文件的解析,即PollingPropertiesFileConfigurationProvider.FileWatcherRunnable.run中的eventBus.post(getCo ...

  3. Hadoop源代码分析(MapReduce概论)

    大家都熟悉文件系统,在对HDFS进行分析前,我们并没有花很多的时间去介绍HDFS的背景,毕竟大家对文件系统的还是有一定的理解的,而且也有很好的文档.在分析Hadoop的MapReduce部分前,我们还 ...

  4. Fabric 1.0源代码分析(17)gossip(流言算法) #deliverclient(deliver客户端)

    # Fabric 1.0源代码笔记 之 gossip(流言算法) #deliverclient(deliver客户端) ## 1.deliverclient概述 deliverclient代码分布在g ...

  5. Hadoop源代码分析 - MapReduce(转载)

    1. Hadoop源代码分析(MapReduce概论) http://caibinbupt.javaeye.com/blog/336467

  6. Hadoop源代码分析

    http://wenku.baidu.com/link?url=R-QoZXhc918qoO0BX6eXI9_uPU75whF62vFFUBIR-7c5XAYUVxDRX5Rs6QZR9hrBnUdM ...

  7. Android Applicaion组件创建的源代码分析(Android 9,含序列图)

    Application组件源代码分析 1. Applicaion启动流程源代码分析 2. 启动过程中应用进程.系统服务进程通信的分界点 3. 组件生命周期与系统服务的关系 4. Application ...

  8. Android系统默认Home应用程序(Launcher)的启动过程源代码分析

    在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还需要有一个Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home应 ...

  9. Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析

    原文地址: http://blog.csdn.net/luoshengyang/article/details/6629298 在前面一篇文章浅谈Android系统进程间通信(IPC)机制Binder ...

最新文章

  1. 如何用一句话证明你学过 NLP ?
  2. springmvc中action跳转
  3. malloc 和new 区别
  4. linux 环境下安装和配置mysql数据库以及远程登录
  5. asp.net 中ashx、axd的区别
  6. elementPlus关闭弹窗,页面原先滚动条消失
  7. 微型计算机组装实验报告虚拟,计算机硬件的组装实验报告.doc
  8. 一道说难不难的js题目
  9. 晨哥真有料丨聊天就在一起了,真有这么简单吗?
  10. 浪潮服务器u盘安装系统未找到任何驱动器,u盘重装win10时找不到任何驱动器
  11. Bithumb Global AMA丨Cred加速实现开放金融——打造区块链上蚂蚁金服
  12. Atitit 源码语句解析结构 目录 1.1. 栈帧(stack frame).每个独立的栈帧一般包括: 1 1.2. 局部变量表(Local Variable Table) 2 2. ref 2
  13. C语言:判断一个三位数是否为水仙花数
  14. Python程序员培训计划
  15. 天猫精灵连接蓝牙摸索1 关于阿里巴巴蓝牙MESH芯片TG7100B LINUX 开发环境塔建图文说明
  16. RCNN系列发展历程
  17. 关于AD18中Board information的位置更改
  18. 【命名规则】驼峰命名法
  19. Java实例教程(上)
  20. CHRE: 编译过程

热门文章

  1. 如何彻底关闭2345流氓软件附带的广告(弹窗,工具栏搜索taskbarsearch,右下角信息展示)
  2. 用Python实现单词尾缀的分析及提取
  3. 简单 python 爬虫(一)
  4. [转载]DbHelper的常用数据库类
  5. pandas美国人口分析实例
  6. python提前查成绩_利用Python来查询自己的成绩!想改成绩吗?我教你啊!
  7. 基于Python的百度AI人脸识别API接口(可用于OpenCV-Python人脸识别)
  8. android GoogleMap画导航线路图 路径规划
  9. 浅谈大数据风控的基本框架
  10. 自动转义json的两种方法