0. 说明

  部分排序 && 全排序 && 采样 && 二次排序


1. 介绍

  sort 是根据 Key 进行排序

  【部分排序】

  在每个分区中,分别进行排序,默认排序即部分排序

  【全排序】

  在所有的分区中,整体有序

  实现全排序的方案:

  1. 使用一个 reduce

  2. 自定义分区函数

  3. 采样

    【3.1 随机采样】

    对于纯文本数据支持不友好
    0. 纯文本建议使用 KeyValueTextInputFormat
    1. 设置分区类 TotalOrderPartition(MR中存在此类 )
    2. 初始化采样器 (RandomSampler) => InputSampler.RandomSampler<Text,Text> sampler = new InputSampler.RandomSampler<Text,Text>(0.01,10);
    3. 设置采样数据地址 => TotalOrderPartitioner.setPartitionFile(job.getConfiguration(),new Path("E:/test/wc/out3"));
    4. 写入采样数据 => InputSampler.writePartitionFile(job,sampler);
    5. 注意1-4步必须写在配置文件之后,job 执行之前

    // new InputSampler.RandomSampler<Text,Text>(0.01,10);
    // 0.01(freq) 每个 Key 被选中的概率
    // 对于每个key都会产生一个0-1之间的浮点数,小于此浮点数的key会被选中
    // 10(numSamples) 样本个数
    // 定义一个10长度的数组,被选中的 Key 回到此数组中
    // 最终从数组中随机选择2个样本

    【3.2 切片采样】

    对每个数据切片取前n个值
    first numSamples / numSplits
    10 / 3

    【3.3 间隔采样】

    每隔一段间隔采样数据 => new InputSampler.IntervalSampler<Text,Text>(0.01);
    对于每个切片样本,当保留的记录数与总记录计数之比小于指定频率时发出

  【二次排序】

  在对 Key 进行排序的基础上,对 Value 进行排序
  1. 重写组合 Key (Compkey) Year+Temp               //在对key进行排序的基础上,对 Value 进行排序
  2. 重写分组对比器,使得在 year 相等的情况下则证明 Compkey 相等  //GroupComparator

  流程图如下

  


2. 全排序(自定义分区函数)

  [2.1 PassMapper.java]

  1. package hadoop.mr.sort.total;
  2.  
  3. import org.apache.hadoop.io.IntWritable;
  4. import org.apache.hadoop.io.LongWritable;
  5. import org.apache.hadoop.io.Text;
  6. import org.apache.hadoop.mapreduce.Mapper;
  7.  
  8. import java.io.IOException;
  9.  
  10. /**
  11. * mapper 类
  12. * 对原始数据进行预处理
  13. */
  14. public class PassMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
  15. @Override
  16. protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
  17.  
  18. // 将 value 变为 String 格式
  19. String line = value.toString();
  20.  
  21. // 将一行文本进行截串
  22. String[] arr = line.split("\t");
  23.  
  24. // 过滤不符合规范的数据
  25. if (arr.length >= 3) {
  26.  
  27. String pass = arr[2];
  28. if (pass != null) {
  29. context.write(new Text(pass), new IntWritable(1));
  30. }
  31. }
  32. }
  33. }

  [2.2 PassReducer.java]

  1. package hadoop.mr.sort.total;
  2.  
  3. import org.apache.hadoop.io.IntWritable;
  4. import org.apache.hadoop.io.Text;
  5. import org.apache.hadoop.mapreduce.Reducer;
  6.  
  7. import java.io.IOException;
  8.  
  9. public class PassReducer extends Reducer<Text,IntWritable,Text,IntWritable> {
  10.  
  11. /**
  12. * 通过迭代所有的key进行聚合
  13. */
  14. @Override
  15. protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
  16.  
  17. int sum = 0;
  18. for(IntWritable value : values){
  19. sum += value.get();
  20. }
  21. context.write(key,new IntWritable(sum));
  22. }
  23. }

  [2.3 PassPartition.java]

  1. package hadoop.mr.sort.total;
  2.  
  3. import org.apache.hadoop.io.IntWritable;
  4. import org.apache.hadoop.io.Text;
  5. import org.apache.hadoop.mapreduce.Partitioner;
  6.  
  7. /**
  8. * 自定义分区实现全排序
  9. */
  10. public class PassPartition extends Partitioner<Text, IntWritable> {
  11. @Override
  12. public int getPartition(Text text, IntWritable intWritable, int numPartitions) {
  13.  
  14. String key = text.toString();
  15. if (key.compareTo("9") < 0) {
  16. return 0;
  17. }
  18. if (key.compareTo("f") < 0) {
  19. return 1;
  20. }
  21. else return 2;
  22.  
  23. }
  24. }

  [2.4 PassApp.java]

  1. package hadoop.mr.sort.total;
  2.  
  3. import org.apache.hadoop.conf.Configuration;
  4. import org.apache.hadoop.fs.FileSystem;
  5. import org.apache.hadoop.fs.Path;
  6. import org.apache.hadoop.io.IntWritable;
  7. import org.apache.hadoop.io.Text;
  8. import org.apache.hadoop.mapreduce.Job;
  9. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
  10. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
  11.  
  12. /**
  13. * 对密码进行全排序
  14. * 通过自定义分区实现全排序
  15. */
  16. public class PassApp {
  17. public static void main(String[] args) throws Exception {
  18. // 初始化配置文件
  19. Configuration conf = new Configuration();
  20.  
  21. // 仅在本地开发时使用
  22. conf.set("fs.defaultFS", "file:///");
  23.  
  24. // 初始化文件系统
  25. FileSystem fs = FileSystem.get(conf);
  26.  
  27. // 通过配置文件初始化 job
  28. Job job = Job.getInstance(conf);
  29.  
  30. // 设置 job 名称
  31. job.setJobName("pass count");
  32.  
  33. // job 入口函数类
  34. job.setJarByClass(PassApp.class);
  35.  
  36. // 设置 mapper 类
  37. job.setMapperClass(PassMapper.class);
  38.  
  39. // 设置 reducer 类
  40. job.setReducerClass(PassReducer.class);
  41.  
  42. // 设置 partition 类
  43. job.setPartitionerClass(PassPartition.class);
  44.  
  45. // 设置 combiner 类
  46. // job.setCombinerClass(PassReducer.class);
  47.  
  48. // 设置分区数量
  49. job.setNumReduceTasks(3);
  50.  
  51. // 设置 map 的输出 K-V 类型
  52. job.setMapOutputKeyClass(Text.class);
  53. job.setMapOutputValueClass(IntWritable.class);
  54.  
  55. // 设置 reduce 的输出 K-V 类型
  56. job.setOutputKeyClass(Text.class);
  57. job.setOutputValueClass(IntWritable.class);
  58.  
  59. // 设置输入路径和输出路径
  60. Path pin = new Path("E:/file/duowan_user.txt");
  61. Path pout = new Path("E:/test/wc/out");
  62. // Path pin = new Path(args[0]);
  63. // Path pout = new Path(args[1]);
  64. FileInputFormat.addInputPath(job, pin);
  65. FileOutputFormat.setOutputPath(job, pout);
  66.  
  67. // 判断输出路径是否已经存在,若存在则删除
  68. if (fs.exists(pout)) {
  69. fs.delete(pout, true);
  70. }
  71.  
  72. // 执行 job
  73. job.waitForCompletion(true);
  74.  
  75. }
  76. }

3. 采样 (随机采样、切片采样、间隔采样)

  [3.1 PassMapper.java]

  1. package hadoop.mr.sort.sampling;
  2.  
  3. import org.apache.hadoop.io.IntWritable;
  4. import org.apache.hadoop.io.LongWritable;
  5. import org.apache.hadoop.io.Text;
  6. import org.apache.hadoop.mapreduce.Mapper;
  7.  
  8. import java.io.IOException;
  9.  
  10. /**
  11. * mapper 类
  12. * 对原始数据进行预处理
  13. */
  14. public class PassMapper extends Mapper<Text, Text, Text, IntWritable> {
  15. @Override
  16. protected void map(Text key, Text value, Context context) throws IOException, InterruptedException {
  17.  
  18. context.write(key, new IntWritable(Integer.parseInt(value.toString())));
  19.  
  20. }
  21. }

  [3.2 PassReducer.java]

  1. package hadoop.mr.sort.sampling;
  2.  
  3. import org.apache.hadoop.io.IntWritable;
  4. import org.apache.hadoop.io.Text;
  5. import org.apache.hadoop.mapreduce.Reducer;
  6.  
  7. import java.io.IOException;
  8.  
  9. public class PassReducer extends Reducer<Text,IntWritable,Text,IntWritable> {
  10.  
  11. /**
  12. * 通过迭代所有的key进行聚合
  13. */
  14. @Override
  15. protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
  16.  
  17. int sum = 0;
  18. for(IntWritable value : values){
  19. sum += value.get();
  20. }
  21. context.write(key,new IntWritable(sum));
  22. }
  23. }

  [3.3 PassApp.java]

  1. package hadoop.mr.sort.sampling;
  2.  
  3. import org.apache.hadoop.conf.Configuration;
  4. import org.apache.hadoop.fs.FileSystem;
  5. import org.apache.hadoop.fs.Path;
  6. import org.apache.hadoop.io.IntWritable;
  7. import org.apache.hadoop.io.Text;
  8. import org.apache.hadoop.mapreduce.Job;
  9. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
  10. import org.apache.hadoop.mapreduce.lib.input.KeyValueTextInputFormat;
  11. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
  12. import org.apache.hadoop.mapreduce.lib.partition.InputSampler;
  13. import org.apache.hadoop.mapreduce.lib.partition.TotalOrderPartitioner;
  14.  
  15. /**
  16. * 对密码进行全排序
  17. * 通过自定义分区实现全排序
  18. * <p>
  19. * 先通过部分排序得到数据
  20. * 将输入路径指向部分排序结果输出路径
  21. */
  22. public class PassApp {
  23. public static void main(String[] args) throws Exception {
  24. // 初始化配置文件
  25. Configuration conf = new Configuration();
  26.  
  27. // 仅在本地开发时使用
  28. conf.set("fs.defaultFS", "file:///");
  29.  
  30. // 初始化文件系统
  31. FileSystem fs = FileSystem.get(conf);
  32.  
  33. // 通过配置文件初始化 job
  34. Job job = Job.getInstance(conf);
  35.  
  36. // 设置 job 名称
  37. job.setJobName("pass count");
  38.  
  39. // job 入口函数类
  40. job.setJarByClass(PassApp.class);
  41.  
  42. // 设置 mapper 类
  43. job.setMapperClass(PassMapper.class);
  44.  
  45. // 设置 reducer 类
  46. job.setReducerClass(PassReducer.class);
  47.  
  48. // 设置 combiner 类
  49. // job.setCombinerClass(PassReducer.class);
  50.  
  51. // 设置全排序采样类 TotalOrderPartitioner.class
  52. job.setPartitionerClass(TotalOrderPartitioner.class);
  53.  
  54. // 设置分区数量
  55. job.setNumReduceTasks(3);
  56.  
  57. // 设置 map 的输出 K-V 类型
  58. job.setMapOutputKeyClass(Text.class);
  59. job.setMapOutputValueClass(IntWritable.class);
  60.  
  61. // 设置 reduce 的输出 K-V 类型
  62. job.setOutputKeyClass(Text.class);
  63. job.setOutputValueClass(IntWritable.class);
  64.  
  65. // 设置输入格式
  66. job.setInputFormatClass(KeyValueTextInputFormat.class);
  67.  
  68. // 设置输入路径和输出路径
  69. Path pin = new Path("E:/test/wc/out");
  70. Path pout = new Path("E:/test/wc/out2");
  71. // Path pin = new Path(args[0]);
  72. // Path pout = new Path(args[1]);
  73. FileInputFormat.addInputPath(job, pin);
  74. FileOutputFormat.setOutputPath(job, pout);
  75.  
  76. /**
  77. * 随机采样,比较浪费性能,耗费资源
  78. * @param freq 每个key被选择的概率 ,大于采样数(2) / 所有key数量(n)
  79. * @param numSamples 所有切片中需要选择的key数量
  80. */
  81. // 设置采样器类型,随机采样
  82. // InputSampler.RandomSampler<Text, Text> sampler = new InputSampler.RandomSampler<Text, Text>(0.01, 10);
  83.  
  84. // 设置采样器类型,切片采样,对有序的数据不友好
  85. // InputSampler.SplitSampler<Text, Text> sampler = new InputSampler.SplitSampler<Text, Text>(10, 3);
  86.  
  87. // 设置采样器类型,间隔采样
  88. InputSampler.IntervalSampler<Text,Text> sampler = new InputSampler.IntervalSampler<Text, Text>(0.01,3);
  89.  
  90. // 设置采样数据地址
  91. TotalOrderPartitioner.setPartitionFile(job.getConfiguration(), new Path("E:/test/wc/out3"));
  92.  
  93. // 写入采样数据
  94. InputSampler.writePartitionFile(job, sampler);
  95.  
  96. // 判断输出路径是否已经存在,若存在则删除
  97. if (fs.exists(pout)) {
  98. fs.delete(pout, true);
  99. }
  100.  
  101. // 执行 job
  102. job.waitForCompletion(true);
  103.  
  104. }
  105. }

4. 二次排序

  [4.1 CompKey.java]

  1. package hadoop.mr.sort.secondary;
  2.  
  3. import org.apache.hadoop.io.WritableComparable;
  4.  
  5. import java.io.DataInput;
  6. import java.io.DataOutput;
  7. import java.io.IOException;
  8.  
  9. /**
  10. * 组合 Key , 包含自定义的排序规则 && 序列反序列化
  11. */
  12. public class CompKey implements WritableComparable<CompKey> {
  13.  
  14. private String year;
  15. private int temp;
  16.  
  17. // 定义排序规则
  18. public int compareTo(CompKey o) {
  19. String oyear = o.getYear();
  20. String tyear = this.getYear();
  21. int otemp = o.getTemp();
  22. int ttemp = this.getTemp();
  23.  
  24. // 如果传入的参数 year 和本身的 year 相同,则比较温度
  25. if (oyear.equals(tyear)) {
  26. return otemp - ttemp;
  27. }
  28. // 年份不同,则返回两个 year 的比较值
  29. return oyear.compareTo(tyear);
  30. }
  31.  
  32. // 串行化
  33. public void write(DataOutput out) throws IOException {
  34. out.writeUTF(year);
  35. out.writeInt(temp);
  36. }
  37.  
  38. // 反串行化
  39. public void readFields(DataInput in) throws IOException {
  40. this.setYear(in.readUTF());
  41. this.setTemp(in.readInt());
  42. }
  43.  
  44. @Override
  45. public String toString() {
  46. return "CompKey{" +
  47. "year='" + year + '\'' +
  48. ", temp=" + temp +
  49. '}';
  50. }
  51.  
  52. public CompKey() {
  53. }
  54.  
  55. public CompKey(String year, int temp) {
  56. this.year = year;
  57. this.temp = temp;
  58. }
  59.  
  60. public String getYear() {
  61. return year;
  62. }
  63.  
  64. public void setYear(String year) {
  65. this.year = year;
  66. }
  67.  
  68. public int getTemp() {
  69. return temp;
  70. }
  71.  
  72. public void setTemp(int temp) {
  73. this.temp = temp;
  74. }
  75. }

  [4.2 MyHashPartition.java]

  1. package hadoop.mr.sort.secondary;
  2.  
  3. import org.apache.hadoop.io.NullWritable;
  4. import org.apache.hadoop.mapreduce.Partitioner;
  5.  
  6. /**
  7. * 自定义 hash 分区
  8. */
  9. public class MyHashPartition extends Partitioner<CompKey, NullWritable> {
  10. public int getPartition(CompKey compKey, NullWritable nullWritable, int numPartitions) {
  11. String year = compKey.getYear();
  12.  
  13. return (year.hashCode() & Integer.MAX_VALUE) % numPartitions;
  14. }
  15. }

  [4.3 SortMapper.java]

  1. package hadoop.mr.sort.secondary;
  2.  
  3. import org.apache.hadoop.io.LongWritable;
  4. import org.apache.hadoop.io.NullWritable;
  5. import org.apache.hadoop.io.Text;
  6. import org.apache.hadoop.mapreduce.Mapper;
  7.  
  8. import java.io.IOException;
  9.  
  10. /**
  11. * Mapper 程序
  12. */
  13. public class SortMapper extends Mapper<LongWritable, Text, CompKey, NullWritable> {
  14. @Override
  15. protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
  16.  
  17. String[] arr = value.toString().split("\t");
  18. String year = arr[0];
  19. int temp = Integer.parseInt(arr[1]);
  20.  
  21. CompKey ck = new CompKey(year, temp);
  22. context.write(ck, NullWritable.get());
  23. }
  24. }

  [4.4 SortReducer.java]

  1. package hadoop.mr.sort.secondary;
  2.  
  3. import org.apache.hadoop.io.IntWritable;
  4. import org.apache.hadoop.io.NullWritable;
  5. import org.apache.hadoop.io.Text;
  6. import org.apache.hadoop.mapreduce.Reducer;
  7.  
  8. import java.io.IOException;
  9.  
  10. /**
  11. * Reducer 程序
  12. */
  13. public class SortReducer extends Reducer<CompKey, NullWritable, Text, IntWritable> {
  14. @Override
  15. protected void reduce(CompKey key, Iterable<NullWritable> values, Context context) throws IOException, InterruptedException {
  16.  
  17. for (NullWritable value : values) {
  18. String year = key.getYear();
  19. int temp = key.getTemp();
  20.  
  21. context.write(new Text(year), new IntWritable(temp));
  22. }
  23. }
  24. }

  [4.5 MyGroupComparator.java]

  1. package hadoop.mr.sort.secondary;
  2.  
  3. import org.apache.hadoop.io.WritableComparable;
  4. import org.apache.hadoop.io.WritableComparator;
  5.  
  6. /**
  7. * 分组对比器,自定义 key 业务逻辑,将 1902 20 1902 30 识别为同一个 key
  8. */
  9. public class MyGroupComparator extends WritableComparator {
  10.  
  11. // 必须写,创建实例必须写 true
  12. protected MyGroupComparator() {
  13. super(CompKey.class, true);
  14. }
  15.  
  16. // 比较算法,只要 year 相等则证明 key 相等
  17. @Override
  18. public int compare(WritableComparable a, WritableComparable b) {
  19. CompKey ck1 = (CompKey) a;
  20. CompKey ck2 = (CompKey) b;
  21.  
  22. return ck1.getYear().compareTo(ck2.getYear());
  23. }
  24. }

  [4.6 SortApp.java]

  1. package hadoop.mr.sort.secondary;
  2.  
  3. import org.apache.hadoop.conf.Configuration;
  4. import org.apache.hadoop.fs.FileSystem;
  5. import org.apache.hadoop.fs.Path;
  6. import org.apache.hadoop.io.IntWritable;
  7. import org.apache.hadoop.io.NullWritable;
  8. import org.apache.hadoop.io.Text;
  9. import org.apache.hadoop.mapreduce.Job;
  10. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
  11. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
  12.  
  13. /**
  14. * 二次排序 App
  15. */
  16. public class SortApp {
  17. public static void main(String[] args) throws Exception {
  18. // 初始化配置文件
  19. Configuration conf = new Configuration();
  20.  
  21. // 仅在本地开发时使用
  22. conf.set("fs.defaultFS", "file:///");
  23.  
  24. // 初始化文件系统
  25. FileSystem fs = FileSystem.get(conf);
  26.  
  27. // 通过配置文件初始化 job
  28. Job job = Job.getInstance(conf);
  29.  
  30. // 设置 job 名称
  31. job.setJobName("Secondary Sort");
  32.  
  33. // job 入口函数类
  34. job.setJarByClass(SortApp.class);
  35.  
  36. // 设置 mapper 类
  37. job.setMapperClass(SortMapper.class);
  38.  
  39. // 设置 reducer 类
  40. job.setReducerClass(SortReducer.class);
  41.  
  42. // 设置自定义分区
  43. job.setPartitionerClass(MyHashPartition.class);
  44.  
  45. // 设置分区数量
  46. job.setNumReduceTasks(3);
  47.  
  48. // 设置分组对比器
  49. job.setGroupingComparatorClass(MyGroupComparator.class);
  50.  
  51. // 设置 map 的输出 K-V 类型
  52. job.setMapOutputKeyClass(CompKey.class);
  53. job.setMapOutputValueClass(NullWritable.class);
  54.  
  55. // 设置 reduce 的输出 K-V 类型
  56. job.setOutputKeyClass(Text.class);
  57. job.setOutputValueClass(IntWritable.class);
  58.  
  59. // 新建输入输出路径
  60. Path pin = new Path("E:/file/kv.txt");
  61. Path pout = new Path("E:/test/wc/out");
  62.  
  63. // 打包后自定义输入输出路径
  64. // Path pin = new Path(args[0]);
  65. // Path pout = new Path(args[1]);
  66.  
  67. // 设置输入路径和输出路径
  68. FileInputFormat.addInputPath(job, pin);
  69. FileOutputFormat.setOutputPath(job, pout);
  70.  
  71. // 判断输出路径是否已经存在,若存在则删除
  72. if (fs.exists(pout)) {
  73. fs.delete(pout, true);
  74. }
  75.  
  76. // 执行 job
  77. job.waitForCompletion(true);
  78. }
  79. }

[MapReduce_7] MapReduce 中的排序的更多相关文章

  1. Hadoop学习笔记—11.MapReduce中的排序和分组

    一.写在之前的 1.1 回顾Map阶段四大步骤 首先,我们回顾一下在MapReduce中,排序和分组在哪里被执行: 从上图中可以清楚地看出,在Step1.4也就是第四步中,需要对不同分区中的数据进行排 ...

  2. MapReduce中的排序(附代码)

    在直接学习hadoop的排序之前还要了解一些基本知识. Hadoop的序列化和比较接口 Hadoop的序列化格式:Writable Writable是Hadoop自己的序列化格式,还要一个子接口是Wr ...

  3. MapReduce中的排序

           hadoop的计算模型就是map/reduce,每一个计算任务会被分割成很多互不依赖的map/reduce计算单元,将所有的计算单元执行完毕后整个计算任务就完成了.因为计算单元之间互不依 ...

  4. Hadoop学习笔记—12.MapReduce中的常见算法

    一.MapReduce中有哪些常见算法 (1)经典之王:单词计数 这个是MapReduce的经典案例,经典的不能再经典了! (2)数据去重 "数据去重"主要是为了掌握和利用并行化思 ...

  5. MapReduce二次排序

    默认情况下,Map 输出的结果会对 Key 进行默认的排序,但是有时候需要对 Key 排序的同时再对 Value 进行排序,这时候就要用到二次排序了.下面让我们来介绍一下什么是二次排序. 二次排序原理 ...

  6. Mapreduce中的字符串编码

    Mapreduce中的字符串编码 $$$ Shuffle的执行过程,需要经过多次比较排序.如果对每一个数据的比较都需要先反序列化,对性能影响极大. RawComparator的作用就不言而喻,能够直接 ...

  7. (转)MapReduce二次排序

    一.概述 MapReduce框架对处理结果的输出会根据key值进行默认的排序,这个默认排序可以满足一部分需求,但是也是十分有限的.在我们实际的需求当中,往往有要对reduce输出结果进行二次排序的需求 ...

  8. MapReduce中一次reduce方法的调用中key的值不断变化分析及源码解析

    摘要:mapreduce中执行reduce(KEYIN key, Iterable<VALUEIN> values, Context context),调用一次reduce方法,迭代val ...

  9. Hadoop学习之路(二十三)MapReduce中的shuffle详解

    概述 1.MapReduce 中,mapper 阶段处理的数据如何传递给 reducer 阶段,是 MapReduce 框架中 最关键的一个流程,这个流程就叫 Shuffle 2.Shuffle: 数 ...

随机推荐

  1. 独享锁 & 共享锁

    独享锁(互斥锁):同时只能有一个线程获得锁.比如,ReentrantLock 是互斥锁,ReadWriteLock 中的写锁是互斥锁. 共享锁:可以有多个线程同时获得锁.比如,Semaphore.Co ...

  2. go sync.once用法

    欢迎关注go语言微信公众号 每日go语言 golang_everyday sync.once可以控制函数只能被调用一次.不能多次重复调用.示例代码: package main import ( &qu ...

  3. Springboot项目打包成jar运行2种方式

    最近公司有个项目需要移植到SpringBoot框架上,项目里面又有许多第三方jar包,在linux服务器上最方便的就是用jar的方式来运行SpringBoot项目了,因此我研究了2种打jar包的方式, ...

  4. ppt提取文字

    ALT+F11调出开发窗口 加入引用 插入模块 输入代码运行 Sub Main() On Error Resume Next Dim temp As New Word.Document, tmpSha ...

  5. mvc导出excel记录

    前言: 记录这篇使用记录,是为了方便以后学习查阅和让没有使用过的人了解一下,其中不足还请见谅.不是很全的文章,大神请绕行.在项目中我们或多或少的会遇到数据导出到excel表格以便线下查看或者记录一些需 ...

  6. mysql常见操作语句,建表,增删改查

    用户操作 新建用户 grant 权限 on 数据库.表名 to 用户名@'访问地址' identified by "密码"; 新建一个可以远程访问数据库的用户 test, 密码:p ...

  7. SQL Server 基本SELECT语句

    1.SELECT 和 FROM 语句 SELECT表示执行的是查询,接着需要更知道从哪边查询数据,FROM就是限制读取的数据在哪一个表或哪几个表中,这样就构成了一个基本语句. SELECT * FRO ...

  8. 外边距塌陷 margin collapsing

    块的顶部外边距和底部外边距有时被组合(折叠)为单个外边距,其大小是组合到其中的最大外边距, 这种行为称为外边距塌陷(margin collapsing),有的地方翻译为外边距合并. 1.相邻的兄弟姐妹 ...

  9. python爬虫入门---第四篇:网站对爬虫的限制及突破测试

    大部分网站对网络爬虫都有限制,限制方式有两种: 一.Robots协议:二.网站通过判断对网站访问http的头部信息来查看是否是爬虫,并对爬虫做相关拦截 第一种限制是书面限制,第二种是强制性阻拦限制.那 ...

  10. 29:ISBN号码

    29:ISBN号码 查看 提交 统计 提问 总时间限制:  1000ms 内存限制:  65536kB 描述 每一本正式出版的图书都有一个ISBN号码与之对应,ISBN码包括9位数字.1位识别码和3位 ...